[User]
I'm looking to create a snowflake generator in OpenSCAD, similar to the one I've been playing with in BlocksCAD. The core idea is to use recursion to create branching structures that look like snowflakes, but I want to have more fine-grained control over the parameters. I have a specific concept I want to achieve, and would like the design to be parametric and highly tunable.

Here's what I have in mind:

1.  **Recursive Branching:** The snowflake should start from a central point and recursively branch out. The recursion depth should be configurable.

2.  **Main Angle Control:** The base angle between the main branches of the snowflake should be adjustable. For example, a main angle of 60 degrees would yield 6 symmetrical branches.

3.  **Branch Angle Control:** I need to control the angle at which the sub-branches grow. This should also be adjustable.

4.  **Branch Length Variation:** The length of the branches should be controlled, possibly with a scale factor or even a random variation to produce an organic feel. I'd like to be able to set a range for how much smaller the children branches are.

5.  **Thickness Variation:** The thickness of the branches should decrease with each level of recursion. I want to define the max and minimum thickness as well as the way the thickness changes from level to level.

6.  **Randomization:** I'd like the option to introduce some randomization into the snowflake generation. This could affect things like branch lengths, positions, and angles, but I want it to be *seeded* so that I can generate the same snowflake again if I want. It should also be possible to disable randomization for a completely predictable result.

7. **Branch Pair Variation:** I want to control how many branch pairs get generated at each position, and I'd also like the number to be varied with randomness or even varied depending on the level.

8. **Branch Position Control**: I want to define how far down a branch the new branches appear at each level, and have that be tunable as well as vary randomly.

9. **Shape Detail:** Currently, each branch uses an extruded rectangle with circles at the ends. I'd like to have the ability to change the shape used for each branch. For example, I'd like to be able to use a polygon with a configurable number of sides for the branch "stick", as well as define the shape of the tips of each branch, like round ends, star ends, or even little spikes.

10. **Extrusion:** Each layer should be extruded to a uniform height.

I'd like to have all these aspects configurable through variables at the top of the file. It should also be easy to adjust these variables and re-render the design.

I would also like it if you used the `math.scad` and `metric.scad` libraries from BOSL2 to assist with trigonometric and unit conversions, especially since I want to be able to specify all angles in degrees. I plan to 3D print the result, so using millimeters as the unit would be ideal.

[Assistant]
random_recursive_snowflake.scad:
```
//!OpenSCAD

iterations = 3;
max_thickness = 0.6;
min_thickness = 0.3;
first_branch_position = 0.25;
last_branch_position = 1;
main_angle = 60;
branch_angle = 60;
height = 2;
thickness_scaling = 1;
// negative for unpredictable
random_seed = 2;

module snowflake() {
  next_seed = random(random_seed);
  for (i = [0 : abs(main_angle) : 359.9999]) {
    rotate([0, 0, i]){
      linear_extrude( height=height, twist=0, scale=[1, 1], center=false){
        branch(0, 1, next_seed);
      }
    }
  }

}

module branch(level, adj_thickness, seed) {
  r1 = random(seed);
  r2 = random(r1);
  r3 = random(r2);
  r4 = random(r3);
  r5 = random(r4);
  next_seed = r5;
  branch_pairs = random_choice(r1, level == 0 ? 2 : 1, 4);
  first_branch_ratio = random_range(r2, 0.2, 0.5);
  last_branch_ratio = random_range(r3, 0.1, 0.2);
  first_branch_position = random_range(r4, 0.1, 0.4);
  last_branch_position = random_range(r5, 0.7, 0.95);
  union(){
    cur_thickness = adj_thickness * interpolate(max_thickness, min_thickness, level / iterations);
    union(){
      translate([(-0.5 * cur_thickness), 0, 0]){
        square([cur_thickness, 30], center=false);
      }
      circle(r=(0.5 * cur_thickness));
      translate([0, 30, 0]){
        circle(r=(0.5 * cur_thickness));
      }
    }
    if (level < iterations) {
      for (i = [0 : abs(1) : branch_pairs - 1]) {
        t = i != 0 ? i / (branch_pairs - 1) : 0;
        translate([0, (30 * interpolate(first_branch_position, last_branch_position, t)), 0]){
          ratio = interpolate(first_branch_ratio, last_branch_ratio, t);
          scale([ratio, ratio, 1]){
            new_adj_thickness = (adj_thickness / ratio) * thickness_scaling;
            union(){
              rotate([0, 0, branch_angle]){
                branch(level + 1, new_adj_thickness, next_seed);
              }
              rotate([0, 0, (0 - branch_angle)]){
                branch(level + 1, new_adj_thickness, next_seed);
              }
            }
          }
        }
      }

    }

  }
}

// guaranteed < 1
function random_choice(seed, from, to) = from + seed % ((to + 1) - from);

function random(seed) = seed < 0 ? round(rands(0,4294967295,1)[0]) : ((seed + 1013904223) * 1664525) % 4294967296;

// guaranteed < 1
function random_fraction(seed) = seed / 4294967296;

// guaranteed < 1
function random_range(seed, from, to) = from + (to - from) * (seed / 4294967296);

function interpolate(a, b, t) = a * (1 - t) + b * t;

snowflake();
```


[User]
Hey, I'm looking to create a customizable door lever adapter that I can 3D print. I've got a round door knob that I want to convert to a lever handle. I've seen a design that does exactly this, and I want to make my own parametric version from scratch, allowing for different knob sizes and lever geometry.

Specifically, the design should consist of two main parts that clamp together around the existing door knob. One part has the lever and the other has a recessed bolt and nut hole for clamping the parts together. The clamping action should be generated with two halves that meet at a small angle and have some rotation to generate compression, as opposed to a simple split down the middle of the handle. Here are some design requirements and parameters I'd like to be able to adjust:

*   **Door Knob Parameters:**
    *   `nom_knob_diam`:  The nominal diameter of the existing door knob (this needs to be very accurate to get a good fit).
    *   `knob_width`: The width of the knob where the clamp connects.
    *   `knob_thick`: The thickness of the clamping area around the knob.
    *   `knob_smooth_rad`:  The radius for smoothing/rounding the knob clamping edges.

*   **Bolt and Nut Parameters:**
    *   `bolt_nomdiam`: The nominal diameter of the bolt to be used for clamping.
    *   `bolt_nomlen`: The nominal length of the bolt.
    *   `bolt_shift_up`:  How much to shift the bolt and nut position up.
    *    `bolt_compressby`: The amount of offset to compress the two halves of the handle for proper clamping.
    *   `bolt_support_angle`: An angle used to design a portion of the handle around the bolt.

*   **Lever Parameters:**
    *  `lever_intersect_angle1`: Angle where the lever starts from the main body of the clamp.
    *  `lever_end_angle`:  The angle to which the end of the lever curves.
    *   `lever_circ1_diam`:  Diameter of the first circular section of the lever.
    *   `lever_circ2_diam`: Diameter of the second circular section of the lever.
    *   `lever_thick`: The thickness of the lever arm.
    *   `lever_width`:  The width of the lever arm.

*   **Smoothing and Splitting Parameters:**
    *   `round_lever_by`:  Radius for rounding out the sharp corners of the lever arm.
    *  `splice_angle`: The angle between the two halves of the clamp where they meet.
    *   `smooth_angle`: The angle increment for smooth circles.
    *   `smooth_size`: The size increment for smooth circles.

*   **Display Parameter**
    *  `display_angle`: The rotation of the part in the preview.

The design should have smooth transitions, using `hull` to blend the different circular shapes for the knob clamp and the lever arm, and also using `rotate_extrude` for smoothing the cylinder edges.  The lever should curve away from the knob and then curve back. It should be split into two pieces, with the split angled to provide compression when the bolt is tightened. The bolt and nut holes should be recessed.

Also, I need the design to be compatible with metric threads, so if you could make sure to pull in a proper thread library that would be awesome. Specifically, it should use the `threads.scad` library available on thingiverse, which includes the `HexAcrossFlats()`, `HexAcrossCorners()`, and `NutThickness()` functions to properly size those features.

I'm expecting the output to be a single .scad file (or multiple if it makes sense to split things up).  I'd really appreciate it if all of these parameters can be easily adjusted at the top of the file.

[Assistant]
door_lever2_customizable.scad:
```
// Created in 2016-2017 by Ryan A. Colyer.
// This work is released with CC0 into the public domain.
// https://creativecommons.org/publicdomain/zero/1.0/
//
// https://www.thingiverse.com/thing:2729961

// Match this value very closely to the desired door knob.
nom_knob_diam = 54.9;

knob_width = 20;
knob_thick = 4;
knob_smooth_rad = 3;

bolt_nomdiam = 5;
bolt_nomlen = 18;
bolt_shift_up = 1;

bolt_compressby = 2;
bolt_support_angle = 24;

lever_intersect_angle1 = -32;
lever_end_angle = 55;
lever_circ1_diam = 80;
lever_circ2_diam = 110;
lever_thick = 20;
lever_width = knob_width;

lever_midrad1 = lever_circ1_diam/2 - lever_thick/2;
lever_midrad2 = lever_circ2_diam/2 - lever_thick/2;

lever2_offx = (lever_midrad1 + lever_midrad2) * cos(lever_intersect_angle1);
lever2_offy = (lever_midrad1 + lever_midrad2) * sin(lever_intersect_angle1);

round_lever_by = 8;

splice_angle = 12;

knob_diam = nom_knob_diam + 1.6;
splice_rotby = 360 * (bolt_compressby / (PI*knob_diam));

bigval = 2 * (lever_circ1_diam + knob_diam + lever_circ2_diam);

knob_outer_rad = knob_diam/2 + knob_thick;

lever_hull_off = 10;
bolt_offset_out = 12 + knob_outer_rad;

display_angle = -105;

// smoothness = 2;  // For fast rendering.
// smoothness = 4;    // For smooth prints.
//smooth_angle = 0.5;
//smooth_size = 0.2;
smooth_angle = 2;
smooth_size = 1.0;

////// copied from threads.scad, https://www.thingiverse.com/thing:1686322 ////

// Provides standard metric hex head widths across the flats.
function HexAcrossFlats(diameter) =
  (diameter <= 64) ?
    lookup(diameter, [
      [2, 4],
      [2.5, 5],
      [3, 5.5],
      [3.5, 6],
      [4, 7],
      [5, 8],
      [6, 10],
      [7, 11],
      [8, 13],
      [10, 16],
      [12, 18],
      [14, 21],
      [16, 24],
      [18, 27],
      [20, 30],
      [22, 34],
      [24, 36],
      [27, 41],
      [30, 46],
      [33, 50],
      [36, 55],
      [39, 60],
      [42, 65],
      [48, 75],
      [52, 80],
      [56, 85],
      [60, 90],
      [64, 95]
    ]) :
    diameter * 95 / 64;

// Provides standard metric hex head widths across the corners.
function HexAcrossCorners(diameter) =
  HexAcrossFlats(diameter) / cos(30);

// Provides standard metric nut thickness.
function NutThickness(diameter) =
  (diameter <= 64) ?
    lookup(diameter, [
      [2, 1.6],
      [2.5, 2],
      [3, 2.4],
      [3.5, 2.8],
      [4, 3.2],
      [5, 4.7],
      [6, 5.2],
      [7, 6.0],
      [8, 6.8],
      [10, 8.4],
      [12, 10.8],
      [14, 12.8],
      [16, 14.8],
      [18, 15.8],
      [20, 18.0],
      [22, 21.1],
      [24, 21.5],
     [27, 23.8],
      [30, 25.6],
      [33, 28.7],
      [36, 31.0],
      [42, 34],
      [48, 38],
      [56, 45],
      [64, 51]
    ]) :
    diameter * 51 / 64;

// This inserts a ClearanceHole with a recessed bolt hole in its children.
// The rotation vector is applied first, then the position translation,
// starting from a position upward from the z-axis at z=0.  The default
// recessed parameters fit a standard metric bolt.
module RecessedClearanceHole(diameter, height, position=[0,0,0], rotation=[0,0,0], recessed_diam=-1, recessed_height=-1, tolerance=0.4) {
  recessed_diam = (recessed_diam < 0) ?
    HexAcrossCorners(diameter) : recessed_diam;
  recessed_height = (recessed_height < 0) ? diameter : recessed_height;
  extra_height = 0.001 * height;

  difference() {
    children();
    translate(position)
      rotate(rotation)
      translate([0, 0, -extra_height/2])
      cylinder(h=height + extra_height, r=(diameter/2+tolerance));
    translate(position)
      rotate(rotation)
      translate([0, 0, -extra_height/2])
      cylinder(h=recessed_height + extra_height/2,
        r=(recessed_diam/2+tolerance));
  }
}

////// end copied from threads.scad ////

module SmoothCylinder(radius, height, smooth_rad) {
  hull() {
    translate([0, 0, smooth_rad])
    rotate_extrude(convexity=10, $fa=smooth_angle, $fs=smooth_size)
      translate([radius-smooth_rad, 0, 0])
      circle(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([0, 0, height-smooth_rad])
    rotate_extrude(convexity=10, $fa=smooth_angle, $fs=smooth_size)
      translate([radius-smooth_rad, 0, 0])
      circle(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
  }
}

module HollowCylinder(outer_rad, inner_rad, height) {
  rad_diff = outer_rad-inner_rad;
  rotate_extrude(convexity=10, $fa=smooth_angle, $fs=smooth_size)
    translate([(outer_rad+inner_rad)/2, 0, 0])
    hull() {
      translate([0, rad_diff/2, 0])
      circle(r=rad_diff/2, $fa=smooth_angle, $fs=smooth_size);
      translate([0, height-rad_diff/2, 0])
      circle(r=rad_diff/2, $fa=smooth_angle, $fs=smooth_size);
    }
}

module SmoothHole(radius, height, smooth_rad,
  position=[0,0,0], rotation=[0,0,0]) {
  extra_height = 0.002 * height;

  render()
  difference() {
    children();
    translate(position)
      rotate(rotation)
      translate([0, 0, -extra_height/2])
      difference() {
        translate([0, 0, -extra_height])
          cylinder(r=radius+smooth_rad, h=height+2*extra_height,
            $fa=smooth_angle, $fs=smooth_size);
        HollowCylinder(radius+2*smooth_rad, radius, height+extra_height);
      }
  }
}

module SmoothCube(dim, smooth_rad) {
  scalex = (smooth_rad < dim[0]/2) ? 1 : dim[0]/(2*smooth_rad);
  scaley = (smooth_rad < dim[1]/2) ? 1 : dim[1]/(2*smooth_rad);
  scalez = (smooth_rad < dim[2]/2) ? 1 : dim[2]/(2*smooth_rad);
  smoothx = smooth_rad * scalex;
  smoothy = smooth_rad * scaley;
  smoothz = smooth_rad * scalez;

  hull() {
    translate([smoothx, smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([smoothx, dim[1]-smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([smoothx, smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, dim[1]-smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([smoothx, dim[1]-smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, dim[1]-smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
  }
}

module NutPocket(nom_diameter, height, position=[0,0,0], rotation=[0,0,0], tolerance=0.4, post_rot_offset=[0,0,0]) {
  pocket_tolerance = pow(3*tolerance/HexAcrossCorners(nom_diameter),2)
      + 0.75*tolerance;

  difference() {
    children();
    translate(position)
      rotate(rotation)
      translate(post_rot_offset)
      translate([0,0,-0.0005*height])
      cylinder(h=height+0.001*height,
        r = (HexAcrossCorners(nom_diameter)+pocket_tolerance)/2, $fn=6);
  }
}

module KnobWrap() {
  SmoothCylinder(knob_outer_rad, knob_width, knob_smooth_rad);
}

module SmoothRing1() {
  SmoothHole(lever_circ1_diam/2 - lever_thick, lever_width,
    round_lever_by)
    SmoothCylinder(lever_circ1_diam/2, knob_width, round_lever_by);
}

module SmoothT() {
  SmoothHole(lever_circ1_diam/2 - lever_thick, lever_width,
    round_lever_by)
  SmoothHole(lever_circ1_diam/2 - lever_thick, lever_width,
    round_lever_by, [0, lever_thick-lever_circ1_diam, 0])
  translate([-lever_circ1_diam/2 + lever_thick - 0.01,
    lever_thick-lever_circ1_diam, 0])
    cube([lever_circ1_diam/2 - lever_thick + 0.02,
      lever_circ1_diam-lever_thick, lever_width]);
}

module LeverBase1() {
  intersection() {
    SmoothRing1();
    translate([0, lever_thick-lever_circ1_diam, 0])
      cube([lever_circ1_diam/2+0.02, lever_circ1_diam-lever_thick,
        lever_width]);
  }
}

module SmoothRing2() {
  SmoothHole(lever_circ2_diam/2 - lever_thick, lever_width,
    round_lever_by)
    SmoothCylinder(lever_circ2_diam/2, knob_width, round_lever_by);
}

module LeverPart1() {
	difference() {
		LeverBase1();

    translate([0, 0, -0.1])
		rotate([0, 0, lever_intersect_angle1])
			cube([lever_circ1_diam, lever_circ1_diam, lever_width+0.2]);
	}
}

module SmoothEndingArc() {
  rotate([0, 0, lever_end_angle])
  union() {
    intersection() {
      SmoothRing2();

      translate([-lever_circ2_diam/2 - lever_thick - 0.1, 0, -0.1])
        cube([lever_circ2_diam + 2*lever_thick + 0.2,
          lever_circ2_diam/2 + lever_thick + 0.1, lever_width+0.2]);
    }

    translate([lever_circ2_diam/2 - lever_thick, -round_lever_by, 0])
      intersection() {
        SmoothCube([lever_thick, 2*round_lever_by, lever_width],
          round_lever_by);
        translate([-0.1, -0.1, -0.1])
          cube([lever_thick+0.2, round_lever_by+0.2, lever_width+0.2]);
      }
  }
}

module LeverPart2() {
  difference() {
    SmoothEndingArc();

    translate([0, 0, -0.1])
      rotate([0, 0, 180 + lever_intersect_angle1 + 0.1])
        cube([lever_circ2_diam/2 + lever_thick + 0.1,
          lever_circ2_diam/2 + lever_thick + 0.1, lever_width+0.2]);
  }
}

module Handle() {
  translate([knob_outer_rad + lever_hull_off,
    lever_circ1_diam/2 - lever_thick/2, 0])
  union() {
    LeverPart1();
    translate([lever2_offx, lever2_offy, 0])
      LeverPart2();
  }
}

module KnobToLever() {
  hull() {
    KnobWrap();
    intersection() {
      Handle();
      translate([0, -lever_thick/2-0.1, -0.1])
        cube([knob_outer_rad + lever_hull_off + 0.01,
          lever_thick+0.2, lever_width+0.2]);
    }
  }
}

module FullDoorLeverUnsplit() {
  RecessedClearanceHole(bolt_nomdiam, knob_diam,
    [bolt_offset_out, lever_midrad1, knob_width/2],
    [90, 0, 0], recessed_height=lever_midrad1 - bolt_nomlen/2
    + NutThickness(bolt_nomdiam/2) - bolt_shift_up)
  NutPocket(bolt_nomdiam, knob_diam,
    [bolt_offset_out, lever_midrad1, knob_width/2],
    [90, 0, 0],
    post_rot_offset=[0, 0, lever_midrad1 + bolt_nomlen/2
    - NutThickness(bolt_nomdiam)/2 - bolt_shift_up])
  union() {
    KnobToLever();
    Handle();
  }
}

module DoorLever() {
  rotate([0, 0, display_angle])
  difference() {
    union() {
      rotate([0, 0, -splice_angle/2])
        difference() {
          FullDoorLeverUnsplit();
          translate([-0.1, 0, -0.1])
            cube([bigval/2, bigval/2, lever_width+0.2]);
        }

      rotate([0, 0, splice_angle/2])
        difference() {
          FullDoorLeverUnsplit();
          scale([1, -1, 1]) translate([-0.1, 0, -0.1])
            cube([bigval/2, bigval/2, lever_width+0.2]);
        }
    }

    cylinder(h=3*knob_width, r=knob_diam/2, center=true, $fa=smooth_angle, $fs=smooth_size);
  }
}

DoorLever();

```
door_lever2.scad:
```
// Created in 2016-2017 by Ryan A. Colyer.
// This work is released with CC0 into the public domain.
// https://creativecommons.org/publicdomain/zero/1.0/
//
// https://www.thingiverse.com/thing:2729961

use <threads.scad>  // Library at https://www.thingiverse.com/thing:1686322

// Match this value very closely to the desired door knob.
nom_knob_diam = 54.9;

knob_width = 20;
knob_thick = 4;
knob_smooth_rad = 3;

bolt_nomdiam = 5;
bolt_nomlen = 18;
bolt_shift_up = 1;

bolt_compressby = 2;
bolt_support_angle = 24;

lever_intersect_angle1 = -32;
lever_end_angle = 55;
lever_circ1_diam = 80;
lever_circ2_diam = 110;
lever_thick = 20;
lever_width = knob_width;

lever_midrad1 = lever_circ1_diam/2 - lever_thick/2;
lever_midrad2 = lever_circ2_diam/2 - lever_thick/2;

lever2_offx = (lever_midrad1 + lever_midrad2) * cos(lever_intersect_angle1);
lever2_offy = (lever_midrad1 + lever_midrad2) * sin(lever_intersect_angle1);

round_lever_by = 8;

splice_angle = 12;

knob_diam = nom_knob_diam + 1.6;
splice_rotby = 360 * (bolt_compressby / (PI*knob_diam));

bigval = 2 * (lever_circ1_diam + knob_diam + lever_circ2_diam);

knob_outer_rad = knob_diam/2 + knob_thick;

lever_hull_off = 10;
bolt_offset_out = 12 + knob_outer_rad;

display_angle = -105;

// smoothness = 2;  // For fast rendering.
// smoothness = 4;    // For smooth prints.
//smooth_angle = 0.5;
//smooth_size = 0.2;
smooth_angle = 2;
smooth_size = 1.0;

module SmoothCylinder(radius, height, smooth_rad) {
  hull() {
    translate([0, 0, smooth_rad])
    rotate_extrude(convexity=10, $fa=smooth_angle, $fs=smooth_size)
      translate([radius-smooth_rad, 0, 0])
      circle(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([0, 0, height-smooth_rad])
    rotate_extrude(convexity=10, $fa=smooth_angle, $fs=smooth_size)
      translate([radius-smooth_rad, 0, 0])
      circle(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
  }
}

module HollowCylinder(outer_rad, inner_rad, height) {
  rad_diff = outer_rad-inner_rad;
  rotate_extrude(convexity=10, $fa=smooth_angle, $fs=smooth_size)
    translate([(outer_rad+inner_rad)/2, 0, 0])
    hull() {
      translate([0, rad_diff/2, 0])
      circle(r=rad_diff/2, $fa=smooth_angle, $fs=smooth_size);
      translate([0, height-rad_diff/2, 0])
      circle(r=rad_diff/2, $fa=smooth_angle, $fs=smooth_size);
    }
}

module SmoothHole(radius, height, smooth_rad,
  position=[0,0,0], rotation=[0,0,0]) {
  extra_height = 0.002 * height;

  render()
  difference() {
    children();
    translate(position)
      rotate(rotation)
      translate([0, 0, -extra_height/2])
      difference() {
        translate([0, 0, -extra_height])
          cylinder(r=radius+smooth_rad, h=height+2*extra_height,
            $fa=smooth_angle, $fs=smooth_size);
        HollowCylinder(radius+2*smooth_rad, radius, height+extra_height);
      }
  }
}

module SmoothCube(dim, smooth_rad) {
  scalex = (smooth_rad < dim[0]/2) ? 1 : dim[0]/(2*smooth_rad);
  scaley = (smooth_rad < dim[1]/2) ? 1 : dim[1]/(2*smooth_rad);
  scalez = (smooth_rad < dim[2]/2) ? 1 : dim[2]/(2*smooth_rad);
  smoothx = smooth_rad * scalex;
  smoothy = smooth_rad * scaley;
  smoothz = smooth_rad * scalez;

  hull() {
    translate([smoothx, smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([smoothx, dim[1]-smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([smoothx, smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, dim[1]-smoothy, smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([smoothx, dim[1]-smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
    translate([dim[0]-smoothx, dim[1]-smoothy, dim[2]-smoothz])
      scale([scalex, scaley, scalez])
      sphere(r=smooth_rad, $fa=smooth_angle, $fs=smooth_size);
  }
}

module NutPocket(nom_diameter, height, position=[0,0,0], rotation=[0,0,0], tolerance=0.4, post_rot_offset=[0,0,0]) {
  pocket_tolerance = pow(3*tolerance/HexAcrossCorners(nom_diameter),2)
      + 0.75*tolerance;

  difference() {
    children();
    translate(position)
      rotate(rotation)
      translate(post_rot_offset)
      translate([0,0,-0.0005*height])
      cylinder(h=height+0.001*height,
        r = (HexAcrossCorners(nom_diameter)+pocket_tolerance)/2, $fn=6);
  }
}

module KnobWrap() {
  SmoothCylinder(knob_outer_rad, knob_width, knob_smooth_rad);
}

module SmoothRing1() {
  SmoothHole(lever_circ1_diam/2 - lever_thick, lever_width,
    round_lever_by)
    SmoothCylinder(lever_circ1_diam/2, knob_width, round_lever_by);
}

module SmoothT() {
  SmoothHole(lever_circ1_diam/2 - lever_thick, lever_width,
    round_lever_by)
  SmoothHole(lever_circ1_diam/2 - lever_thick, lever_width,
    round_lever_by, [0, lever_thick-lever_circ1_diam, 0])
  translate([-lever_circ1_diam/2 + lever_thick - 0.01,
    lever_thick-lever_circ1_diam, 0])
    cube([lever_circ1_diam/2 - lever_thick + 0.02,
      lever_circ1_diam-lever_thick, lever_width]);
}

module LeverBase1() {
  intersection() {
    SmoothRing1();
    translate([0, lever_thick-lever_circ1_diam, 0])
      cube([lever_circ1_diam/2+0.02, lever_circ1_diam-lever_thick,
        lever_width]);
  }
}

module SmoothRing2() {
  SmoothHole(lever_circ2_diam/2 - lever_thick, lever_width,
    round_lever_by)
    SmoothCylinder(lever_circ2_diam/2, knob_width, round_lever_by);
}

module LeverPart1() {
	difference() {
		LeverBase1();

    translate([0, 0, -0.1])
		rotate([0, 0, lever_intersect_angle1])
			cube([lever_circ1_diam, lever_circ1_diam, lever_width+0.2]);
	}
}

module SmoothEndingArc() {
  rotate([0, 0, lever_end_angle])
  union() {
    intersection() {
      SmoothRing2();

      translate([-lever_circ2_diam/2 - lever_thick - 0.1, 0, -0.1])
        cube([lever_circ2_diam + 2*lever_thick + 0.2,
          lever_circ2_diam/2 + lever_thick + 0.1, lever_width+0.2]);
    }

    translate([lever_circ2_diam/2 - lever_thick, -round_lever_by, 0])
      intersection() {
        SmoothCube([lever_thick, 2*round_lever_by, lever_width],
          round_lever_by);
        translate([-0.1, -0.1, -0.1])
          cube([lever_thick+0.2, round_lever_by+0.2, lever_width+0.2]);
      }
  }
}

module LeverPart2() {
  difference() {
    SmoothEndingArc();

    translate([0, 0, -0.1])
      rotate([0, 0, 180 + lever_intersect_angle1 + 0.1])
        cube([lever_circ2_diam/2 + lever_thick + 0.1,
          lever_circ2_diam/2 + lever_thick + 0.1, lever_width+0.2]);
  }
}

module Handle() {
  translate([knob_outer_rad + lever_hull_off,
    lever_circ1_diam/2 - lever_thick/2, 0])
  union() {
    LeverPart1();
    translate([lever2_offx, lever2_offy, 0])
      LeverPart2();
  }
}

module KnobToLever() {
  hull() {
    KnobWrap();
    intersection() {
      Handle();
      translate([0, -lever_thick/2-0.1, -0.1])
        cube([knob_outer_rad + lever_hull_off + 0.01,
          lever_thick+0.2, lever_width+0.2]);
    }
  }
}

module FullDoorLeverUnsplit() {
  RecessedClearanceHole(bolt_nomdiam, knob_diam,
    [bolt_offset_out, lever_midrad1, knob_width/2],
    [90, 0, 0], recessed_height=lever_midrad1 - bolt_nomlen/2
    + NutThickness(bolt_nomdiam/2) - bolt_shift_up)
  NutPocket(bolt_nomdiam, knob_diam,
    [bolt_offset_out, lever_midrad1, knob_width/2],
    [90, 0, 0],
    post_rot_offset=[0, 0, lever_midrad1 + bolt_nomlen/2
    - NutThickness(bolt_nomdiam)/2 - bolt_shift_up])
  union() {
    KnobToLever();
    Handle();
  }
}

module DoorLever() {
  rotate([0, 0, display_angle])
  difference() {
    union() {
      rotate([0, 0, -splice_angle/2])
        difference() {
          FullDoorLeverUnsplit();
          translate([-0.1, 0, -0.1])
            cube([bigval/2, bigval/2, lever_width+0.2]);
        }

      rotate([0, 0, splice_angle/2])
        difference() {
          FullDoorLeverUnsplit();
          scale([1, -1, 1]) translate([-0.1, 0, -0.1])
            cube([bigval/2, bigval/2, lever_width+0.2]);
        }
    }

    cylinder(h=3*knob_width, r=knob_diam/2, center=true, $fa=smooth_angle, $fs=smooth_size);
  }
}

DoorLever();

```


[User]
Hey, I'm looking to create a customizable 2D steamboat design that can be laser cut or 3D printed. I'd like it to be based on an origami style, where it has an outer outline and some inner outlines defining the details.

Specifically, I need a way to control:

*   **Maximum Size:** I want to be able to specify the maximum dimension of the model (either width or height) and have the other dimension scale proportionally to maintain the original aspect ratio of the steamboat. So like, if I give it a max size of 150, it will auto scale to 150 in whatever direction is longer.
*   **Model Height:** This controls the overall thickness of the steamboat.
*   **Outline Thicknesses:** I need to independently control the thickness of the outer outline (the very edge) and the inner outlines, those that give the steamboat its details.
*   **Inner Outline Roundness:** The inner outlines should have rounded ends, and I need to be able to specify the radius of this rounding.
*   **Flipping:** I'd like an option to flip the design horizontally, like a mirror image.

I have a basic idea of the steamboat's shape and internal details that I can provide as a set of 2D coordinates.

I'm thinking a single .scad file should be enough to do this, but feel free to break it up if it makes more sense. It should be parametric so I can easily adjust these settings via the customizer in OpenSCAD. I don't need any specialized libraries or anything to do this, I think it should be fine with just regular OpenSCAD modules.

[Assistant]
customizable_origami_steamboat.scad:
```
/*
 * Customizable Origami - Steamboat - https://www.thingiverse.com/thing:2730388
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-12-25
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

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

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.6; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.8; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.9; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [494, 707];
A2 = [579, 512];
A3 = [643, 504];
A4 = [658, 432];
A5 = [780, 426];
A6 = [779, 486];
A7 = [892, 470];
A8 = [913, 217];
A9 = [1014, 215];
A10 = [1103, 442];
A11 = [1214, 431];
A12 = [1353, 632];
A13 = [1784, 619];
A14 = [1752, 722];
A15 = [1425, 1040];
A16 = [1348, 1052];
A17 = [532, 1052];
A18 = [371, 1052];
A19 = [211, 728];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19];

// Inner Points
B1 = [702, 695];
B2 = [1172, 662];

// Polygons
C1 = [A8, A9, A10, A7];
C2 = [A4, A5, A6, A3];
C3 = [A2, A3, A6, A7, A10, A11, A12, B2, B1, A1];
C4 = [A19, A18, A17, B1, A1];
C5 = [B1, B2, A16, A17];
C6 = [B2, A12, A13, A14, A15, A16];

cut_polygons = [C1, C2, C3, C4, C5, C6];

min_x = A19[0];
min_y = A9[1];
max_x = A13[0];
max_y = A18[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
I need an OpenSCAD model for a flange that I can customize for various projects, particularly for covering gaps around pipes and vents. I'd like it to be parametric so I can adjust the key dimensions.

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

*   **Outer Flange Shape:** I need to be able to define the number of sides for the outer flange (e.g., a circle, hexagon, octagon, etc.). The number of sides should range from 4 to 60.
*   **Inner Flange Shape:** I want to control the number of sides of the inner flange too, using the same range of 4 to 60.
*   **Outer Diameter (OD):** The overall diameter of the outer flange in millimeters, adjustable from 50 to 300 mm.
*   **Inner Diameter (ID):** The diameter of the hole in the center of the flange, adjustable from 25 to 200 mm.
*   **Flange Height:** The thickness of the flange, from 3 to 10 mm.
*   **Tube Center Height:** The height of the "tube" that comes out of the center of the flange (think a short cylindrical portion extending upwards), adjustable between 3 and 50 mm.
*   **Bolt Hole Diameter:** The diameter of the holes for mounting bolts, adjustable from 3 to 12 mm.
*   **Bolt Hole Spread:** The center-to-center diameter of the bolt holes circle, adjustable from 25 to 250 mm.
*   **Number of Bolt Holes:** The number of bolt holes around the circle, from 2 to 24.

The flange should have the central cylinder extending upwards, and bolt holes placed in a circular pattern around it. I'd like to be able to change all of these parameters easily in the customizer.

I'm not sure if this will need libraries but I am not expecting any, so a "vanilla" version would be helpful.

[Assistant]
Flange_Model_v3.scad:
```
/*
*  Open SCAD Name.: flange_model_v3.scad
*  Copyright (c)..: 2017 www.DIY3DTech.com
*
*  Creation Date..:12/24/2017
*  Discription....: general flange model
*
*  Rev 1: Developed Model
*  Rev 2: Multi-shaped flange
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  If used commercially attribution is required (OpenSCAD.DIY3DTech.com)
*
*/

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

/*---------------------Parameters---------------------*/

// outer flange shape
flange_shape_outside    =    4;      //[4:1:60]
// outer flange shape
flange_shape_inside     =   60;      //[4:1:60]
// outer diameter in (mm)
flange_OD               =  150;      //[50:1:300]
// inter diameter in (mm)
flange_ID               =   75;      //[25:1:200]
// flange height in (mm)
flange_height           =    3;      //[3:1:10]
// tube center height
center_height           =   25;      //[3:1:50]
// opening diameter in (mm)
bolt_ID                 =    6;      //[3:1:12]
// center to center spread of retaining bolts (mm)
bolt_spread             =   95;      //[25:1:250]
// number of bolts in flange
bolt_count              =    4;      //[2:1:24]

/*-----------------------Execute----------------------*/

flange();

/*-----------------------Modules----------------------*/

module flange() {
    difference(){
        //create flange
         union() {
            cylinder(flange_height,flange_OD/2,flange_OD/2,$fn=flange_shape_outside,true);
            translate([0, 0,center_height/2]) cylinder(center_height,(flange_ID+flange_height)/2,(flange_ID+flange_height)/2,$fn=flange_shape_inside,true);
         }
        //remove center
        translate([0, 0,center_height/2]) cylinder(center_height*2,flange_ID/2,flange_ID/2,$fn=flange_shape_inside,true);

        //create circle pattern
        for (i=[0:(360/bolt_count):360]) {
            //theta is degrees set by for loop from 0 to 360 (degrees)
            theta=i;
            //this sets the x axis point based on the COS of the theta
            x=0+(bolt_spread/2)*cos(theta);
            //this sets the y axis point based on the sin of the theta
            y=0+(bolt_spread/2)*sin(theta);
            //this creates the circle or other obect at the x,y point
            translate([x,y,0]) cylinder(flange_height+2,bolt_ID/2,bolt_ID/2,$fn=60,true);;
        }//end for loop for circle creation

    }//end differance
}//end module

/*---------------------End Program--------------------*/
```


[User]
Hey, I'm looking to create a customizable origami-style goat design in OpenSCAD, similar to the one I found on Thingiverse (thing:2730789). I'd like to be able to control a few key things:

*   **Overall Size:** I need to set the maximum size of the longest side (either width or height), and the other dimension should scale proportionally to maintain the aspect ratio of the goat.  I'm thinking in millimeters, so this value should also be interpreted in millimeters.

*   **Model Height:** I need to set the thickness of the model, also in millimeters.

*   **Outline Thickness:** I need two thicknesses here: one for the outer outline, and another for the inner lines/cuts. The values should also be in millimeters.

*   **Inner Outline Radius:**  I want to be able to control the roundness of the ends of the inner lines/cuts. This radius should also be settable in millimeters.

*   **Flip Option:** I need a simple toggle to be able to flip the whole model horizontally. I don't need any other mirroring/rotation beyond that.

I'd like the goat itself to be constructed from a series of points and polygons. The main outline should be defined by a set of points, and then the inner cuts should be defined by a set of closed polygons with those same points and some internal ones. I can provide the exact coordinates of the points (I have the file handy), if you like.

The code should use the `polygon` module along with `offset` and `linear_extrude`, and also allow for mirroring. I'm fine with hardcoding the point data into the file. I don't think I'll need any external libraries for this, but if you have an idea of using one, let me know. I think a single `.scad` file should be sufficient for this.

Can you help me generate this code?

[Assistant]
customizable_origami_goat.scad:
```
/*
 * Customizable Origami - Goat - https://www.thingiverse.com/thing:2730789
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-12-25
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

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

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.4; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.7; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.3; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [373, 618];
A3 = [634, 351];
A4 = [713, 353];
A5 = [838, 293];
A6 = [975, 290];
A7 = [829, 350];
A8 = [997, 349];
A9 = [828, 402];
A10 = [1017, 628];
A11 = [1085, 637];
A12 = [1353, 635];
A13 = [1522, 757];
A14 = [1542, 907];
A15 = [1450, 783];
A16 = [1391, 804];
A17 = [1493, 1236];
A18 = [1388, 1236];
A19 = [1413, 1189];
A20 = [1056, 950];
A21 = [688, 1236];
A22 = [642, 1236];
A23 = [548, 1236];
A24 = [629, 1192];
A25 = [681, 618];
A26 = [660, 618];
A27 = [495, 780];
A28 = [493, 618];

outline = [A1, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28];

// Inner Points
B2 = [718, 434];
B3 = [549, 618];
B4 = [976, 672];
B5 = [715, 618];
B6 = [787, 410];

// Polygons
C1 = [A5, A6, A7, A4];
C2 = [A3, B2, B6, A9, A8, A7, A4];
C3 = [A3, A1, B2];
C5 = [A1, B2, B5, A25, A26, B3, A28];
C6 = [A28, A27, B3];
C7 = [B3, A26, A27];
C8 = [B2, B6, B4, A22, A24, A25, B5];
C9 = [A24, A22, A23];
C10 = [A22, B4, A10, A11, A20, A21];
C11 = [A9, A10, B4, B6];
C12 = [A11, A12, A16, A17, A19, A20];
C13 = [A12, A13, A15, A16];
C14 = [A15, A13, A14];
C15 = [A19, A18, A17];

cut_polygons = [C1, C2, C3, C5, C6, C7, C8, C9, C10, C11, C12, C13, C14, C15];

min_x = A1[0];
min_y = A6[1];
max_x = A14[0];
max_y = A21[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
Okay, I need a parametric OpenSCAD design for a replacement washer for a ukulele friction tuner. I've got a pretty good idea of the geometry I need, and I'm going to give you the measurements I've taken. I'd like it to be customizable using parameters so I can tweak it if necessary.

Here's the basic idea: it's a truncated cone with a conical hole inside of it, and a cylindrical hole through the center. The outside cone flares out at the base, and the inner cone flares out at the top.

Here's what I've got for my specific uke's measurements. Everything is in millimeters:

*   **Overall height** of the washer: 3.4 mm
*   **Outer diameter at the base**: 11.4 mm
*   **Outer diameter at the top**: 10.3 mm
*   **Height of the inner cone**: 2.1 mm
*   **Inner diameter at the top**: 9.9 mm
*   **Inner diameter at the base**: 4.35 mm
*   **Diameter of the pin** that goes through the center hole: 4.23 mm

I'd like to be able to adjust all of those dimensions as parameters. Also, the pin hole needs some clearance so the part fits on the pin; I was thinking something around 0.4mm, so can you make the pin diameter plus a clearance value a configurable parameter as well?

Finally, I'd like the design to include some built in parameter validation with warnings. For instance, I'd like a warning if the base outer diameter is less than or equal to the top outer diameter. Likewise, I'd need warnings if the inner cone has the wrong flare, and if any of the heights are wonky. Oh, and also validation to make sure the inner diameters are sized correctly relative to the pin diameter.

I don't think any special libraries are required for this. Thanks!

[Assistant]
uke-tuner-washer.scad:
```
// -- Uke Friction Tuner Washer Replacement -- //
// -- Simple OpenSCAD Script by Ryan Pavlik -- //

// If you're editing this by hand:
// Ignore stuff that is in brackets - that's special comments for Thingiverse Customizer to parse
// Also, sorry for the long lines in some of the comments, hopefully you're editing using the
// built-in editor in OpenSCAD or another editor with soft word-wrap. Customizer can only
// deal with one comment line per parameter.

// -- Configuration Parameters -- //
// These are what I measured for mine, but they might not be right for you.
// Calipers time! Units are mm.

// *     Define the Outside      * //
// the yellow surfaces in OpenSCAD //
/* [outside] */

// Overall height of the desired washer - measure existing washer.
height = 3.4;

// Diameter of outside of washer at base (uke head) - measure existing washer.
outerBaseDiameter = 11.4;

// Diameter of outside of washer at top (furthest from head) - measure existing washer.
outerTopDiameter = 10.3;

// *      Define the Inside Cone     * //
// (sloped green surfaces in OpenSCAD) //
/* [inside-cone] */

// height of inner sloped portion - measure on tuner key
innerHeight = 2.1;

// Diameter of inner portion at the top and presumably widest part of the key - measure on key.
innerTopDiameter = 9.9;

// Diameter of inner portion at the base and presumably narrowest part of the key - measure on key.
innerBaseDiameter = 4.35;

// *   Define the Inside Cylinder   * //
// (other green surfaces in OpenSCAD) //
/* [inside-cylinder] */

// Largest diameter of the pin that goes through this. Clearance gets added automatically. It's important that this is the *largest* diameter because the pin probably has flat sides - but we want a round hole.
pinDiameter = 4.23;

// -- shouldn't need to edit below here -- //

/* [Advanced-slash-Troubleshooting] */
// value added to pin diameter for clearance. one 0.4mm nozzle-width seemed OK to me: a little snug, but you wouldn't want it loose. You probably don't need to change this unless you printed one and you either can't fit it on the pin, or it's a sloppy fit on the pin..
pinClearance = 0.4;

/* [hidden] */
// Let's really-well approximate these circles.
$fa = 5;
$fs = 0.1;
// for Customizer - what tab is open? used to highlight.
preview_tab = "";
// For Customizer - to make messages readable, since the real geometry is radially symmetric
// preview[view:south, tilt:top]

// for messages - some unicode characters
GT = "\u003E";
LEQ = "\u2264";
GEQ = "\u2265";

// -- parameter validation and warning messages -- //
// Parameter validation: diameters of each cone.
// Outer cone flares at base.
if (outerBaseDiameter <= outerTopDiameter)
    warning("outerBaseDiameter", LEQ, "outerTopDiameter", "You may have your parameters flip-flopped. Outer cone should flare at the bottom in this design.");

// Inner cone flares at top.
if (innerTopDiameter <= innerBaseDiameter)
    warning("innerTopDiameter", LEQ, "innerBaseDiameter", "You may have your parameters flip-flopped. Inner cone should flare at the top in this design.");

// Parameter validation: heights
if (height <= innerHeight)
    warning("height", LEQ, "innerHeight", "You almost certainly have at least one of these values wrong, or the values swapped: the inner cone shouldn't be taller than the entire object overall.");

// Parameter validation: Pin vs inner base diameter - I almost made this one just modify the inner base diameter to be the same as the pin diameter, since in practice they'll be very close, but...
if (pinDiameter > innerBaseDiameter)
    warning("pinDiameter", GT, "innerBaseDiameter", "By the measuring instructions, innerBaseDiameter should be at least pinDiameter.");

// Parameter validation: Pin diameter vs other diameters that it should be definitely smaller than.
if (pinDiameter >= innerTopDiameter)
    warning("pinDiameter", GEQ, "innerTopDiameter", "At least one of these values is likely to be wrong.");
if (pinDiameter >= outerTopDiameter)
    warning("pinDiameter", GEQ, "outerTopDiameter", "At least one of these values is likely to be wrong.");
if (pinDiameter >= outerBaseDiameter)
    warning("pinDiameter", GEQ, "outerTopDiameter", "At least one of these values is likely to be wrong.");

// The actual geometry: One truncated cone, with another truncated cone and a cylinder
// subtracted from it.
// "difference()" takes the first solid as a "positive",
// and subtracts all additional solids (2 in this case) from it, as "negatives".
// Ignore the "highlight()" stuff - that's just to make Customizer on Thingiverse work better.
difference() {
    // Uses of this small "epsilon" value scattered here are just to push surfaces past each other
    // for the boolean ops, so we don't have faces coinciding when performing differences.
    // Sometimes not strictly necessary, but it makes the preview more usable and
    // the math done by the software more reliable.
    epsilon = 0.01;

    // This line creates the outer truncated cone.
    highlight("outside") cylinder(d1 = outerBaseDiameter, d2 = outerTopDiameter, h = height);

    // The remaining lines then subtract from that initial solid.
    // First, these 3 lines create a truncated cone at the top (hence translate),
    // to match the slope on the key
    highlight("inside-cone") translate([0,0, height - innerHeight + epsilon]) {
        cylinder(d1 = innerBaseDiameter, d2 = innerTopDiameter, h = innerHeight + epsilon);
    }
    // These lines create a simple (right) cylinder (not a cone) all the way through for the pin.
    // Made it taller than the positive, and translated it down, so faces don't coincide.
    highlight("inside-cylinder") translate([0,0, -epsilon]) {
        cylinder(d = pinDiameter + pinClearance, h = height + 2 * epsilon);
    }
}

// Used to highlight in Customizer preview. Ignore otherwise.
// Based on Customizer docs.
module highlight(this_tab) {
    if (preview_tab == this_tab) {
        color("red") children();
    } else {
        children();
    }
}

module warning(var1, relation, var2, details) {
    // full message only seen if not in customizer.
    condition = str(var1, " ", relation, " ", var2);
    fullmsg = str("<b>WARNING</b>: ", condition, " -- ", details);
    echo(fullmsg);
    if (preview_tab != "") {
        !translate([-15, 10, 0]) {
            text(str("WARNING: ", condition), size = 1);
        }
    }
}
```


[User]
I'd like a parametric OpenSCAD design for a stylized swallow, similar to the one described in the Thingiverse link you may have. I need to be able to adjust the overall size, the height, and the thickness of both the inner and outer outlines. There are several parameters I want to control.

Specifically:

*   **Max Size:** I want to be able to specify the maximum dimension of the design (either width or height). The other dimension should scale proportionally to maintain the swallow's aspect ratio. I'm thinking I'd like a parameter called `max_size_in_millimeter` to control that, and it should be adjustable between 20 and 300 mm.
*   **Model Height:** This should be how thick the resulting object is, and it should be adjustable via a parameter called `model_height_in_millimeter`, between 0.2 and 100 mm in steps of 0.2.
*   **Outer Outline Thickness:** I need to control the thickness of the outermost line of the swallow, using a parameter named `outline_size_in_millimeter`. The default of the provided code appears to be around 1.6, and I want to allow that to range from 0.5 to 20mm in steps of 0.1.
*   **Inner Outline Thickness:** There are internal lines within the swallow design; I need to adjust their thickness using a parameter called `inline_size_in_millimeter`. I'd like it to also range between 0.5 and 20 mm in steps of 0.1 and the default of the provided code appears to be around 0.8.
*   **Inner Outline Edge Radius:** Finally, the ends of the inner lines should be rounded, and I need to control that with `inline_edge_radius_in_millimeter`. It should be adjustable from 0 to 5 mm in steps of 0.1 and the default of the provided code appears to be around 0.3.
*   **Flip Model:** Finally I would like a toggle switch called `flip_model` with two options: `"yes"` and `"no"`. This would flip the swallow over its horizontal axis. The default should be `"no"`.

The design itself is based on a set of outer points defining the overall shape and a set of inner points that are connected to create cut-out sections. I'm providing the specific coordinates from the original file for you to use. Here are the outer points (named A1 through A30) :

```
A1 = [399, 1666];
A2 = [354, 1199];
A3 = [365, 1105];
A4 = [438, 1000];
A5 = [662, 765];
A6 = [577, 649];
A7 = [595, 615];
A8 = [559, 553];
A9 = [625, 571];
A10 = [645, 540];
A11 = [815, 606];
A12 = [1058, 361];
A13 = [1225, 278];
A14 = [1334, 273];
A15 = [1667, 305];
A16 = [1319, 564];
A17 = [1338, 578];
A18 = [1254, 768];
A19 = [1166, 811];
A20 = [1112, 817];
A21 = [1186, 931];
A22 = [1758, 1221];
A23 = [1265, 1077];
A24 = [1450, 1598];
A25 = [1114, 1057];
A26 = [950, 1010];
A27 = [916, 1146];
A28 = [700, 1300];
A29 = [681, 1282];
A30 = [650, 1298];
```

And here are the inner points (named B1 through B18) :

```
B1 = [900, 785];
B2 = [986, 744];
B3 = [1083, 770];
B4 = [876, 971];
B5 = [838, 888];
B6 = [550, 1123];
B7 = [685, 933];
B8 = [405, 1087];
B9 = [1152, 451];
B10 = [1052, 545];
B11 = [1114, 415];
B12 = [950, 874];
B13 = [1019, 835];
B14 = [900, 822];
B15 = [942, 784];
B16 = [1188, 321];
//B17 = [698, 1255];  // B17 is commented out in the original
B18 = [494, 1035];
```

These points are used to define polygons (named C1 through C24):

```
C1 = [A1, B6, A30];
C2 = [B6, A30, A29];
C3 = [B6, B18, B8, A2, A1];
C4 = [A2, A3, B8];
C5 = [B6, B7, B18];
C6 = [A3, A4, A5, B5, B7, B18, B8];
C7 = [B6, B18, B7, B5, B4, A27, A28, A29];
C8 = [A30, A29, A28];
C9 = [B4, A26, A27];
C10 = [B5, B1, B14, B12, A25, A26, B4];
C11 = [B1, B2, B3, A20, A21, B13, B15];
C12 = [B3, A18, A19, A20];
C13 = [B2, B10, B11, B9, A16, A17, A18, B3];
C14 = [A5, B1, A11, A10, A9, A7, A6];
C15 = [A8, A7, A9];
C16 = [A5, B5, B1];
C17 = [A11, B2, B1];
C18 = [A11, A12, A13, B16, B11, B10, B2];
C19 = [B16, A14, A13];
C20 = [B10, B11, B9];
C21 = [B11, B16, A14, A15, B9];
C22 = [B9, A16, A15];
C23 = [B1, A23, A24, A25, B12, B14];
C24 = [B1, B15, B13, A21, A22, A23];
```

The basic process should be to create the overall outline, then subtract the inner polygons, with appropriate offsets for the inner and outer line thicknesses. It appears that some polygons may be commented out, it is okay if these are excluded from the final design. I'd like a single .scad file.

Let me know if you need anything else!

[Assistant]
customizable_origami_swallow.scad:
```
/*
 * Customizable Origami - Swallow - https://www.thingiverse.com/thing:2732659
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-12-26
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

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

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.6; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.8; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.3; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [399, 1666];
A2 = [354, 1199];
A3 = [365, 1105];
A4 = [438, 1000];
A5 = [662, 765];
A6 = [577, 649];
A7 = [595, 615];
A8 = [559, 553];
A9 = [625, 571];
A10 = [645, 540];
A11 = [815, 606];
A12 = [1058, 361];
A13 = [1225, 278];
A14 = [1334, 273];
A15 = [1667, 305];
A16 = [1319, 564];
A17 = [1338, 578];
A18 = [1254, 768];
A19 = [1166, 811];
A20 = [1112, 817];
A21 = [1186, 931];
A22 = [1758, 1221];
A23 = [1265, 1077];
A24 = [1450, 1598];
A25 = [1114, 1057];
A26 = [950, 1010];
A27 = [916, 1146];
A28 = [700, 1300];
A29 = [681, 1282];
A30 = [650, 1298];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30];

// Inner Points
B1 = [900, 785];
B2 = [986, 744];
B3 = [1083, 770];
B4 = [876, 971];
B5 = [838, 888];
B6 = [550, 1123];
B7 = [685, 933];
B8 = [405, 1087];
B9 = [1152, 451];
B10 = [1052, 545];
B11 = [1114, 415];
B12 = [950, 874];
B13 = [1019, 835];
B14 = [900, 822];
B15 = [942, 784];
B16 = [1188, 321];
//B17 = [698, 1255];
B18 = [494, 1035];

// Polygons
C1 = [A1, B6, A30];
C2 = [B6, A30, A29];
C3 = [B6, B18, B8, A2, A1];
C4 = [A2, A3, B8];
C5 = [B6, B7, B18];
C6 = [A3, A4, A5, B5, B7, B18, B8];
C7 = [B6, B18, B7, B5, B4, A27, A28, A29];
C8 = [A30, A29, A28];
C9 = [B4, A26, A27];
C10 = [B5, B1, B14, B12, A25, A26, B4];
C11 = [B1, B2, B3, A20, A21, B13, B15];
C12 = [B3, A18, A19, A20];
C13 = [B2, B10, B11, B9, A16, A17, A18, B3];
C14 = [A5, B1, A11, A10, A9, A7, A6];
C15 = [A8, A7, A9];
C16 = [A5, B5, B1];
C17 = [A11, B2, B1];
C18 = [A11, A12, A13, B16, B11, B10, B2];
C19 = [B16, A14, A13];
C20 = [B10, B11, B9];
C21 = [B11, B16, A14, A15, B9];
C22 = [B9, A16, A15];
C23 = [B1, A23, A24, A25, B12, B14];
C24 = [B1, B15, B13, A21, A22, A23];

cut_polygons = [C1, C2, C3, C4, /*C5,*/ C6, C7, /*C8,*/ C9, C10, C11, C12, C13, C14, C15, C16, C17, C18, C19, /*C20,*/ C21, C22, C23, C24];

min_x = A2[0];
min_y = A14[1];
max_x = A22[0];
max_y = A1[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User]
I'd like to create a customizable, snowflake-like design using OpenSCAD. The idea is based on a kirigami concept, where you're essentially folding a piece of paper into 12ths and then making cuts, which when unfolded create a radial pattern. I'm looking for a parametric design, so I can easily tweak the size and complexity.

Here are the core parameters I'd like to control:

*   **`main_radius`**: The overall radius of the snowflake, setting its size.
*   **`min_snips` & `max_snips`**: The minimum and maximum number of "snips" or cuts that will be created along the edges of the base triangle.
*   **`height`**: The extrusion height of the final 2D shape.
*   **`radial_variability`**: A value from 0 to 1 which controls the extent to which the innermost point of the base triangle can vary radially from the center (0 means the innermost point is fixed at `main_radius`, 1 would allow it to sit directly at the center of the design.
*   **`snip_depth_variability`**: A value from 0 to 1 controlling how deep the individual snips go inward. 0 would make the snips extend to the guide point and 1 would make them sit at the midpoint of the line they're based on.
*   **`outer_snip_size`**: A value between 0 and 1 that controls how far toward the center the outer snips go. 0 means they're at the edge, 1 means they're at the center.
*   **`min_outer_snips` & `max_outer_snips`**: The minimum and maximum number of "snips" that are made along the main edge of the base triangle.

The design is based on a main triangle, which is then used to generate the snowflake via rotation. The base triangle's points are:
    *   Point 1: along a radius that is randomly within `radial_variability` from the `main_radius`, and along the X axis.
    *   Point 2: at a fixed 30-degree angle with respect to the X axis, at the `main_radius` radius.
    *   Point 3: a point part way between points 1 and 2. This should vary randomly so every snowflake is slightly different from every other.
From each edge of this base triangle, I want to add random "snips" (basically smaller triangle cutouts), with the depth and quantity controlled by the parameters outlined earlier. The final model should be a linear extrusion of this shape. Please ensure that the points are defined in a way that makes sure the snips don't self intersect or go outside of the main triangle.

Also, I think it would be nice if the design used `rands()` rather than `random()` to allow users to generate predictable results. I don't expect this design to be compatible with the customizer - as such, error handling is not especially needed.

I think that should cover everything - let me know if I've missed anything. I'm looking forward to seeing what you can come up with!

[Assistant]
kiri-final.scad:
```
//!OpenSCAD
main_radius = 60;
min_snips = 1;
max_snips = 5;
height = 2;
// all variability from 0 to 1
radial_variability = 0.7;
snip_depth_variability = 0.5;
// between 0 and 1
outer_snip_size = 0.1;
min_outer_snips = 0;
max_outer_snips = 5;

module tri(x1, y1, x2, y2, x3, y3, r) {
  for (i = [0 : abs(60) : 359]) {
    rotate([0, 0, i]){
      union(){
        mirror([1,0,0]){
          hull(){
            translate([x1, y1, 0]){
              circle(r=r);
            }
            translate([x2, y2, 0]){
              circle(r=r);
            }
            translate([x3, y3, 0]){
              circle(r=r);
            }
          }
        }
        hull(){
          translate([x1, y1, 0]){
            circle(r=r);
          }
          translate([x2, y2, 0]){
            circle(r=r);
          }
          translate([x3, y3, 0]){
            circle(r=r);
          }
        }
      }
    }
  }

}

function maxi(x, y) = x > y ? x : y;

function mini(x, y) = x < y ? x : y;

function random_range(a, b) = interpolate(a, b, rands(0,1,1)[0]);

function interpolate(a, b, t) = a * (1 - t) + b * t;

module main_triangle() {
  x1 = random_range(main_radius * (1 - radial_variability), main_radius * 1);
  x2 = cos(30) * main_radius;
  y2 = sin(30) * main_radius;
  divider = rands(0,1,1)[0];
  x3 = interpolate(x1, x2, divider);
  y3 = interpolate(0, y2, divider);
  difference() {
    tri(x1, 0, x2, y2, 0, 0, 0.003);

    snips(0, 0, x1, 0, x3, y3, min_snips, max_snips);
    snips(0, 0, x2, y2, x3, y3, min_snips, max_snips);
    snips(x1, 0, x2, y2, interpolate(x2, 0, outer_snip_size), interpolate(y2, 0, outer_snip_size), min_outer_snips, max_outer_snips);
  }
}

module snips(base_x, base_y, main_x, main_y, guide_x, guide_y, min_snips, max_snips) {
  num_snips = round(rands(min_snips,max_snips,1)[0]);
  if (num_snips > 0) {
    for (i = [1 : abs(1) : num_snips]) {
      snip(i, num_snips, base_x, base_y, main_x, main_y, guide_x, guide_y);
    }

  }

}

module snip(i, n, base_x, base_y, main_x, main_y, guide_x, guide_y) {
  a = (i - 1) / n;
  b = i / n;
  r1 = random_range(a, b);
  r2 = random_range(a, b);
  t1 = mini(r1, r2);
  t2 = maxi(r1, r2);
  t3 = random_range(a, b);
  x1 = interpolate(base_x, main_x, mini(r1, r2));
  y1 = interpolate(base_y, main_y, mini(r1, r2));
  x2 = interpolate(base_x, main_x, maxi(r1, r2));
  y2 = interpolate(base_y, main_y, maxi(r1, r2));
  x3 = interpolate(base_x, guide_x, t3);
  y3 = interpolate(base_y, guide_y, t3);
  t4 = random_range(1 - snip_depth_variability, 1);
  tri(x1, y1, x2, y2, interpolate((x1 + x2) / 2, x3, t4), interpolate((y1 + y2) / 2, y3, t4), 0.05);
}

linear_extrude( height=height, twist=0, scale=[1, 1], center=false){
  main_triangle();
}
```


[User]
I'd like to create a customizable keychain using OpenSCAD. The keychain should have a rounded rectangular shape with text engraved into the surface, and a hole for attaching to a keyring. I’ve got some specific ideas about how I’d like this to work:

First, the overall shape. I want a rectangle with rounded ends, kind of like a pill shape, but with the rounded parts being half-circles. The ends should be circular, and the rectangle part should join directly to them, so it's all one smooth shape. I want to be able to adjust the overall length, width, and thickness of the keychain body, and also control the radius of the rounded corners.

Second, I need to be able to engrave text on the top of the keychain. I'd like to be able to specify the text, the font, the font style (like bold or italic), the height of the text, and the spacing between characters. I’d want to use standard fonts like Helvetica, Times, Arial, Courier, and also have the option to use a Google font. I will specify the font name directly in that case. I would like the text centered.

Third, the keyring hole. I want a cylindrical hole through one end of the rounded part. I'd like it to be centered in the rounded part and pass completely through the keychain. Also I'd like it to have an additional smaller round cut on the outside edge of the key chain, like a chamfer. I will probably want the hole to be about the same width as the keychain thickness. I want a setting to adjust that as well.

Finally, I'd like to have a global variable to set `$fn`. Could you write the OpenSCAD code for this? I don't need any external libraries.

[Assistant]
key-chain_v1_2.scad:
```
$fn=100;

/*
 * Personal Key Chain
 *   v1.1
 *   December 2015
 *   by Jonathan Meyer (jon@stej.com)
 *
 *   v1.2
 *   December 2017
 *   by Timo aka Timothy McPrint (https://www.thingiverse.com/Timothy_McPrint/)
 */

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

// The name to display on the key chain.
name = "Test text!";

/* [Font] */

// The font to write the name in.
font = 1; // [0:Google (give name below), 1:Helvetica, 2:Times, 3:Arial, 4:Courier]

// the name of the font to get from https://www.google.com/fonts/
google_font = "Arial Unicode MS";

// The Font Style. Not all styles work with all fonts.
style = 4; // [0:None, 1:Regular, 2:Bold, 3:Italic, 4:Bold Italic]

// The spacing between the characters (1.0 is standard):
text_spacing = 1.0;

//Height of the text:
text_height = 15;

/* [Size] */

// How tall the key chain will be.
width = 20; // [15:30]

// How long the key chain will be.
length = 90;  // [40:100]

// How thick the key chain will be.
thickness = 5; // [3:10]

// How round the corners will be
roundness = 2;   // [1:5]

/* [Hidden] */

font_list = [undef,
             "Helvetica",
             "Times",
             "Arial Unicode MS",
             "Courier",
             "Symbola",
             "Apple Color Emoji",
             "serif",
             "Code2001"];

style_list = [undef,
              "Regular",
              "Bold",
              "Italic",
              "Bold Italic"];

w = width;
l = length;
d = thickness;
r = roundness;
t = str(name);

fn = (font == 0) ? google_font : font_list[font];
sn = (style_list[style] != undef) ? str(":style=",style_list[style]) : "";
f = str(fn,sn);

echo(str("Font Used -> ", f));

union()
{
	difference()
	{
		hull( convexity=10 )
		{
			translate( [-((w/2)+(l/2)-2.5),0,0] )
			{
				minkowski( convexity=10 )
				{
					cylinder( h=d-r, d=w, center=true );
					sphere( d=r );
				}
			}

			translate( [l/2,0,0] )
			{
				minkowski( convexity=10 )
				{
						cylinder( h=d-r, d=w, center=true );
						sphere( d=r );
				}
			}

			minkowski( convexity=10 )
			{
				cube( [l,w,d-r],true );
				sphere( d=r );
			}
		}

		translate( [-((w/2)+(l/2)-2.5),0,0] )
		{
			difference()
			{
				cylinder( h=d*2, d=w, center=true, convexity=10 );
				rotate_extrude( convexity=10 )
					translate( [(w-d+r)/2,0,0] )
						minkowski( convexity=10 )
						{
							square( [d-r, d-r], true );
							circle( d=r );
						}
			}
		}

		linear_extrude( height=d/2+0.1, convexity=10 )
			text( t, valign="center", halign="center", font=f, script="dingbats", size=text_height, spacing=text_spacing );
	}
}

```


[User]
Alright, I'm trying to make a cool snowflake-like design in OpenSCAD, and I'm having trouble getting started. I've seen some examples using mathematical functions to create interesting curves, like in that Quanta article about math and art, and I'd really like to base my design on that idea.

I'm thinking of a parametric design, where I can tweak a few key parameters and get different results.

Specifically, I want to generate a 2D curve by using a formula like:

`x = cos(t) + cos(7*t)/2 - sin(17*t)/3`
`y = sin(t) + sin(7*t)/2 - cos(17*t)/3`

where 't' ranges from 0 to 360 degrees. I want to take the (x,y) coordinates generated by this function and apply a non-linear scaling to them based on their distance from the origin. Something like:

`r = sqrt(x^2 + y^2)`
`scaled_x = r^power * x`
`scaled_y = r^power * y`

Then, I'll use those scaled coordinates to place a small circle at each point along the curve. To generate a continuous curve, I'd like to use a `hull()` command to connect adjacent circles.

Finally, I want to reflect the entire curve across the y-axis to complete the snowflake shape, and then extrude the whole shape to make it 3D.

Here are the parameters I'd like to control:

*   `scale2`: A general scaling factor applied to the entire design.
*   `width`: The radius of the circles used to draw the curve.
*   `thickness`: The extrusion height (z-axis)
*   `power`: The exponent used in the non-linear scaling function; this is the most important parameter for me to play around with. I want to see how different values will affect the final design.

Could you generate the OpenSCAD code for me? I don't need any special libraries for this, just the standard OpenSCAD functions.

[Assistant]
Parametric_Curvy_Snowflake.scad:
```
//!OpenSCAD
scale2 = 30;
width = 2;
thickness = 2;
power = 1.5;
linear_extrude( height=thickness, twist=0, scale=[1, 1], center=false){
  union(){
    curve();
    mirror([1,0,0]){
      curve();
    }
  }
}

module curve() {
  // chain hull
  for (t = [0 : abs(1) : 360 - 1]) {
    hull() {
    point_at(t);
    point_at((t + 1));
    }  // end hull (in loop)
   } // end loop

}

module point_at(t) {
  x = (cos(t) + cos((7 * t)) / 2) - sin((17 * t)) / 3;
  y = (sin(t) + sin((7 * t)) / 2) - cos((17 * t)) / 3;
  r = sqrt(pow(x, 2) + pow(y, 2));
  translate([(scale2 * (pow(r, power) * x)), (scale2 * (pow(r, power) * y)), 0]){
    circle(r=width);
  }
}

```


[User]
I'd like to design a tool holder for my small file set. I've got a set of files that didn't come with any sort of case, and I want something I can screw into the wall or workbench. I'm thinking it should have multiple slots for the files, and it needs to be parametric so I can change the number of slots and the diameter of the file shafts.

Here are the specific requirements:

*   **Number of Slots:** I want to be able to specify how many slots the holder has. This should be a variable. I'm thinking something between 1 and 20 would be good.
*   **Tool Diameter:** The diameter of the file shafts needs to be configurable.  I'd like to set it in millimeters, and a range between 2 and 16 mm should be more than enough.
*   **Wall Mount:** The holder will need to attach to a wall or bench. This means I'll need mounting holes, and I'd like two per section.
*  **Screw Size:** I plan on using "size 6" wood screws, which means the mounting holes should be countersunk. The head is about 6.5mm wide at the top, and the countersink should be appropriate for that.
*   **Slot Design:** The slots should have a sort of rounded "keyhole" shape, with a larger diameter at the top for easy insertion. The smaller diameter should be a little bigger than the tool shaft, so the file is held securely.
*   **Modular Design:** I need a single unit and then repeated for each slot. This way I can do different things (like put countersink holes on only the ends).

I think the design should have a main "wallplate" that screws to the wall or bench, and "hanger" that holds the files.  I want the slot to be an expanded cylinder, and a rectangle removed to create a keyhole. The parts should be easily joined so I can repeat them in a single file.

I would like this all implemented as an OpenSCAD file or multiple files. I'm not picky about which approach you take so long as it is easily usable with the OpenSCAD Customizer. I'm new to CAD design, so I don't want to have to mess around with a lot of code. I would prefer to keep it simple and be able to generate STL files easily. No libraries are necessary.

[Assistant]
_p_toolbracket.scad:
```
//wall/bench hook for file set

//how many tools
slots = 6; //[1:20]
//diameter of the shafts, in mm
tool_diam = 3; //[2:16]

//secret vars
$fn = 50 * 1;
trad = tool_diam / 2;
sw = trad * 1.2;
//6.3 is a magic number, it's 2 screw holes wide
//...assuming you use size 6 wood screws
min_wid = 6.3 + tool_diam * 2;
cen = (min_wid / 2);

module wallplate(){
    difference(){
        cube([4, min_wid, 20]);
    }
}
module hanger(){
    difference(){
        cube ([tool_diam * 5, min_wid, 4]);
        translate ([tool_diam * 2, cen, -0.5]) slot();
    }
}
module slot(){
    union(){
        cylinder(h = 5, r1 = tool_diam, r2 = sw);
        translate ([0, -(trad), 0]) cube([50, tool_diam, 50]);
    }
}
module single(){
    rotate ([0, 350, 0]) hanger();
    translate ([-3, 0, 0]) wallplate();
}
rotate ([0, 270, 0]) for(i=[0:slots-1]){
    translate ([0, i * min_wid, 0]){
        //single();
        if (i == 0){
            difference(){
                single();
                translate ([-3.5, cen, 12]) rotate ([0, 90, 0]) cylinder(h = 5, r1 = 1.5, r2 = 3.15);
            }
        } else if (i == slots - 1){
            difference(){
                single();
                translate ([-3.5, cen, 12]) rotate ([0, 90, 0]) cylinder(h = 5, r1 = 1.5, r2 = 3.15);
            }
        } else {
            single();
        }
    }
}
```


[User]
I'd like to create a customizable 2D camel design in OpenSCAD, similar to the one described in the provided text. I need a parametric design that allows me to adjust several key features.

Specifically, I need the following adjustable parameters:

*   **`max_size_in_millimeter`**: This will control the overall size of the camel. The longest side of the camel will be set to this value, and the shorter side will scale proportionally to maintain the aspect ratio. The user should be able to input a value between 20 and 300, probably with integer steps.
*   **`model_height_in_millimeter`**: This sets the extrusion height of the camel. It should have a range from 0.2 to 100, with a step size of 0.2 (float values)
*   **`outline_size_in_millimeter`**: This parameter controls the thickness of the outer edge of the camel. It should be adjustable from 0.5 to 20 with steps of 0.1.
*   **`inline_size_in_millimeter`**: This controls the thickness of the inner cut lines of the camel. It should also be adjustable from 0.5 to 20 with steps of 0.1.
*   **`inline_edge_radius_in_millimeter`**: This sets the radius of the rounded edges on the inner cut lines, with a range from 0.0 to 5 and steps of 0.1.
*   **`flip_model`**: A simple string option with "yes" or "no" to control whether the camel is mirrored across the Y axis before final render.

The camel's shape is defined by a set of outer points (A1-A21) and inner polygons formed from those points (C1-C11). The inner polygons should be cut out of the outer shape.

I'll need OpenSCAD code that calculates the necessary scaling factors and dimensions based on the user-defined `max_size_in_millimeter`, and then constructs the camel shape using these calculations. Specifically, it needs to:
1.  Determine the bounding box of the camel shape based on its outline points (A1-A21)
2.  Calculate the scaling factors necessary to make the longest side match `max_size_in_millimeter`.
3.  Resize the camel based on those scaling factors.
4.  Use the `outline_size`, `inline_size`, and `inline_edge_radius` parameters to create the required cutouts.
5.  Use linear extrude for the thickness specified by `model_height_in_millimeter`.
6.  Optionally flip (mirror) the shape if `flip_model` is set to "yes".

Also, please make sure `$fn` is set to 32.

I'm expecting a single .scad file that includes all the necessary code, with no external libraries required.

[Assistant]
customizable_origami_camel.scad:
```
/*
 * Customizable Origami - Camel - https://www.thingiverse.com/thing:2734204
 * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about
 * created 2017-12-27
 * version v1.0
 *
 * Changelog
 * --------------
 * v1.0:
 *      - final design
 * --------------
 *
 * This work is licensed under the Creative Commons - Attribution - NonCommercial - ShareAlike license.
 * https://creativecommons.org/licenses/by-nc-sa/3.0/
 */

 // Parameter Section //
//-------------------//

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

// The maximum size of the longest side. The shorter side will be automatically resized in same ratio.
max_size_in_millimeter = 120; //[20:300]

// The height of the model.
model_height_in_millimeter = 5; //[0.2:0.2:100]

// Thickness of the outer outline.
outline_size_in_millimeter = 1.9; //[0.5:0.1:20]

// Thickness of the inner outline.
inline_size_in_millimeter = 0.8; //[0.5:0.1:20]

// Roundiness of the ends of the inner outlines.
inline_edge_radius_in_millimeter = 0.4; //[0.0:0.1:5]

// Flip model
flip_model = "no"; //[yes,no]

/*[hidden]*/
max_size = max_size_in_millimeter;
model_height = model_height_in_millimeter;
$fn=32;

// Outer Points
A1 = [430, 222];
A2 = [459, 156];
A3 = [560, 101];
A4 = [672, 97];
A5 = [757, 209];
A6 = [790, 480];
A7 = [1169, 94];
A8 = [1540, 480];
A9 = [1542, 533];
A10 = [1673, 854];
A11 = [1567, 1004];
A12 = [1543, 591];
A13 = [1529, 1200];
A14 = [1165, 825];
A15 = [810, 1200];
A16 = [800, 857];
A17 = [593, 866];
A18 = [429, 733];
A19 = [406, 509];
A20 = [586, 281];
A21 = [499, 202];

outline = [A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21];

// Inner Points
B1 = [666, 181];
B2 = [1176, 480];

// Polygons
C1 = [A1, A2, A3];
C2 = [A1, A3, A4, B1, A20, A21];
C3 = [A4, A5, B1];
C4 = [B1, A5, A6, A18, A19, A20];
C5 = [A18, A6, A16, A17];
C6 = [A6, B2, A7];
C7 = [A7, A8, B2];
C8 = [A6, A14, A15, A16];
C9 = [A6, B2, A8, A14];
C10 = [A14, A8, A9, A12, A13];
C11 = [A9, A10, A11, A12];

cut_polygons = [C1, C2, C3, C4, C5, C6, C7, C8, C9, C10, C11];

min_x = A19[0];
min_y = A7[1];
max_x = A10[0];
max_y = A15[1];

x_size = max_x - min_x;
y_size = max_y - min_y;

x_factor = max_size / x_size;
y_factor = max_size / y_size;

inline_size = x_size > y_size ? inline_size_in_millimeter / x_factor / 2: inline_size_in_millimeter / y_factor / 2;
inline_edge_radius = x_size > y_size ? inline_edge_radius_in_millimeter / x_factor: inline_edge_radius_in_millimeter / y_factor;
outline_size = x_size > y_size ? outline_size_in_millimeter / x_factor - inline_size: outline_size_in_millimeter / y_factor - inline_size;

 // Program Section //
//-----------------//

if(x_size > y_size) {
    resize(newsize=[max_size, x_factor * y_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
} else {
    resize(newsize=[y_factor * x_size, max_size, model_height]){
        if(flip_model == "yes") {
            mirror([0,1,0]) {
                rotate([180,180,0]) {
                    create(outline, cut_polygons);
                }
            }
        } else {
            create(outline, cut_polygons);
        }
    }
}

 // Module Section //
//----------------//

module create() {
    linear_extrude(height=1) {
        difference() {
            offset(r = -0) {
                offset(r = +outline_size) {
                    polygon(points = outline, convexity = 10);
                }
            }

            for(cut_polygon = [0:len(cut_polygons)]) {
                offset(r = +inline_edge_radius) {
                    offset(r = -inline_size - inline_edge_radius) {
                        polygon(points = cut_polygons[cut_polygon], convexity = 10);
                    }
                }
            }
        }
    }
}

```


[User] I'm looking to create a parametric tripod stand for a small Christmas tree using OpenSCAD. I'd like the design to be flexible, allowing for either round or square rods to be used as the central support. The user should be able to specify the rod's diameter (or diagonal, if it's a square rod), the length of the tripod legs, the leg thickness, and whether to add pads at the end of the legs for better stability. I'd also like a parameter to control the height of the tripod, which should be relative to the leg length, and an option to have a closed or open bottom. Finally, there should be an option to make the legs extra strong. Could you generate the OpenSCAD code for this? I'm not using any external libraries, but if you have suggestions, I'm open to it.

[Assistant]
rod_tripod.scad:
```
// Rod Tripod
// Design by Marius Gheorghescu, December 2017

// diameter of the rod or diagonal (!) or the square rod
rod_dia = 20.0;

// length of a leg
size = 80;

// thickness of the legs (recommended: minimum 6x extrusion width)
wall_thickness = 2.46;

// has round pads at the end of legs (0:off, 0.1-0.2: breakable, 1.0: full)
leg_pads = 0.9;

// set to 1 to re-inforce the legs
extra_strong = 0.0;

// ratio of height to leg length (0.5 recommended)
height_ratio = 0.5;

// set to closed bottom
closed_bottom = 1;

// set to 1 if square rod
square_rod = 0;

/* [Hidden] */

height = size * height_ratio;
epsilon = 0.001;

module leg(leg_pads)
{
    $fn = 50;

    union() {
        hull() {
            translate([0,0, height/2])
                cylinder(r=min(wall_thickness,rod_dia/2), h=height, center=true);

            translate([size,0,wall_thickness/2])
                cylinder(r=wall_thickness/2, h=wall_thickness, center=true);
        }

        hull() {
            translate([size - rod_dia/2 + wall_thickness,0,wall_thickness/2*leg_pads])
                cylinder(r=min(size/4, rod_dia), h=wall_thickness*leg_pads, center=true);

            if (extra_strong) {
                translate([0,0, height - wall_thickness/2 - epsilon])
                    cylinder(r=(rod_dia + 2*wall_thickness)/2, h=wall_thickness, center=true);

                translate([0,0, wall_thickness/2])
                    cylinder(r=wall_thickness/2, h=wall_thickness, center=true);
            }
        }

    }

}

difference() {

    $fn=square_rod?4:30;

    // legs
    union() {

        // center rod support
        translate([0,0, height/2])
            rotate([0,0,45])
            cylinder(r=(rod_dia/2 + wall_thickness), h=height, center=true);

        leg(leg_pads);

        rotate([0,0,120])
            leg(leg_pads);

        rotate([0,0,-120])
            leg(leg_pads);
    }

    // rod
    translate([0,0, height/2 + closed_bottom*wall_thickness])
        rotate([0,0,45])
        cylinder(r=rod_dia/2, h=height + epsilon, center=true);

}

//%translate([0,0,100/2])cube([8,8,100], center=true);

```


[User]
I'm looking to create a customizable, randomized snowflake generator in OpenSCAD. I've got some ideas on how I want it to work and some parameters I want to be able to control. It should be a recursive design, meaning branches sprout from branches. I'd like to be able to control several aspects of the snowflake:

*   **Iterations:** This will control the depth of the recursion (how many levels of branching)
*   **Thickness:** I want to specify a maximum and minimum thickness for the branches, with the branches getting thinner as you go out to new generations of branches
*   **Branch Positions:** The point along the branch where new sub-branches begin can be controlled by a first and last position along a line (interpolate between these based on which index the branch is).
*   **Branch Ratio:** The ratio of sub-branch size to parent branch size, again interpolated between first and last ratios based on index.
*   **Main Angle:** The angle between the main branches of the snowflake (e.g. 60 degrees would make a six-sided flake).
*   **Branch Pairs:** The number of branch pairs that come off of each branch (at a given level), with a configurable minimum and maximum.
*   **Branch Angle:** The angle between the two branches of a branch pair
*   **Height:** A base height for the branches
*   **Thickness Scaling:** A way to scale thickness of new generations of branches
*   **Line Style Changeover Level:** the level at which the branches change from cylindrical lines to lines with expanded node shapes at either end and in the middle.
*   **Node Width and Slope:** parameters to control the size of the expanded nodes and how much they are sloped off of the ends of the branches
*   **Random Seed:** A seed that controls the overall random number generation for the parameters, or a negative value to get random behaviour

I'd like to use some randomization in the design, mainly around:
*   The number of branch pairs
*   The branch positions, interpolated between first and last
*   The branch ratio, interpolated between first and last

For the random seed, I need a pseudorandom function to derive random values from the seed in a repeatable fashion (so it changes with the seed but remains the same given the same seed), and a random range function that produces numbers within a range. Also, a function for selecting one of a range of integers.
The design should be created entirely from scratch, so no libraries needed. It would also be great if you could make the code well-commented so it's easy to understand and change later.

[Assistant]
leafy_snowflake_randomized.scad:
```
//!OpenSCAD

iterations = 2;
max_thickness = 0.6;
min_thickness = 0.3;
first_branch_position = 0.2;
last_branch_position = 0.8;
min_branch_ratio = 0.25;
max_branch_ratio = 0.6;
main_angle = 60;
min_branch_pairs = 2;
max_branch_pairs = 5;
branch_angle = 40;
height = 2;
thickness_scaling = 1;
line_style_changeover_level = 1;
node_width = 3;
node_slope = 30;
// negative for unpredictable
random_seed = -1;

module snowflake() {
  new_seed = random(random_seed);
  for (i = [0 : abs(main_angle) : 359.9999]) {
    rotate([0, 0, i]){
      branch(0, 1, new_seed);
    }
  }

}

module branch(level, adj_thickness, seed) {
  r1 = random(seed);
  r2 = random(r1);
  r3 = random(r2);
  branch_pairs = random_choice(r1, min_branch_pairs, max_branch_pairs);
  first_branch_ratio = random_range(r2, min_branch_ratio, max_branch_ratio);
  last_branch_ratio = random_choice(r3, min_branch_ratio, max_branch_ratio);
  new_seed = r3;
  cur_thickness = adj_thickness * interpolate(max_thickness, min_thickness, level / iterations);
  union(){
    line(level, cur_thickness, adj_thickness);
    if (level < iterations) {
      for (i = [0 : abs(1) : branch_pairs - 1]) {
        subbranch(i, branch_pairs, adj_thickness, first_branch_ratio, last_branch_ratio, new_seed, level);
      }

    }

  }
}

module subbranch(i, branch_pairs, adj_thickness, first_branch_ratio, last_branch_ratio, seed, level) {
  t = i / (branch_pairs - 1);
  ratio = interpolate(first_branch_ratio, last_branch_ratio, t);
  new_seed = seed;
  new_adj_thickness = (adj_thickness / ratio) * thickness_scaling;
  translate([0, (30 * interpolate(first_branch_position, last_branch_position, t)), 0]){
    scale([ratio, ratio, ratio]){
      union(){
        rotate([0, 0, branch_angle]){
          branch(level + 1, new_adj_thickness, new_seed);
        }
        rotate([0, 0, (0 - branch_angle)]){
          branch(level + 1, new_adj_thickness, new_seed);
        }
      }
    }
  }
}

function random(seed) = seed < 0 ? round(rands(0,4294967295,1)[0]) : ((seed + 1013904223) * 1664525) % 4294967296;

// guaranteed < 1
function random_fraction(seed) = seed / 4294967296;

// guaranteed < 1
function random_range(seed, from, to) = from + (to - from) * (seed / 4294967296);

// guaranteed < 1
function random_choice(seed, from, to) = from + seed % ((to + 1) - from);

function interpolate(a, b, t) = a * (1 - t) + b * t;

module line(level, cur_thickness, scale_adjust) {
  adj_height = height * scale_adjust;
  intersection() {
    union(){
      if (level > line_style_changeover_level) {
        linear_extrude( height=adj_height, twist=0, scale=[1, 1], center=false){
          hull(){
            circle(r=0.1);
            translate([node_width, 10, 0]){
              circle(r=0.1);
            }
            translate([(0 - node_width), 10, 0]){
              circle(r=0.1);
            }
            translate([0, 30, 0]){
              circle(r=0.1);
            }
          }
        }
      }

      if (level <= line_style_changeover_level) {
        hull(){
          cylinder(r1=cur_thickness, r2=cur_thickness, h=adj_height, center=false);
          translate([0, 30, 0]){
            cylinder(r1=cur_thickness, r2=cur_thickness, h=adj_height, center=false);
          }
        }
      }

    }

    translate([0, 0, adj_height]){
      rotate([0, node_slope, 0]){
        translate([-50, 0, -100]){
          cube([100, 100, 100], center=false);
        }
      }
    }

    translate([0, 0, adj_height]){
      rotate([0, (0 - node_slope), 0]){
        translate([-50, 0, -100]){
          cube([100, 100, 100], center=false);
        }
      }
    }

  }
}

snowflake();
```