[User]
Hey, I'm looking to get some OpenSCAD files designed for a teabag holder, and I want it to be highly customizable. I've got a pretty good idea of the general shape, and I've got some inspiration that should really help.

Basically, I want a teabag holder that has three main sections: a base, a middle section with teeth (or hooks) to hold the bag, and a tapered top section. The base will be wider than the middle, and the top will be narrower. I also want a ring around it all for support and to raise it up off the bottom of the cup, along with handles. It's kind of hard to explain without drawing it, so I'll give you the parameters I was thinking of and describe some of the relationships between them.

**Here are the parameters:**

*   `$wanddicke`: Overall wall thickness (e.g., 1.4mm).
*   `$aussenring_wanddicke`: Wall thickness for the outer ring (e.g., 1.5mm).
*   `$halter_durchmesser_mittelstueck`: Diameter of the middle section with the teeth (e.g., 56mm).
*   `$halter_durchmesser_differenz_basis`: Diameter difference between the middle and base (e.g., 9mm, added to the middle).
*   `$halter_durchmesser_basis`: Diameter of the base (calculated from middle diameter and difference).
*   `$halter_durchmesser_differenz_verjuengung`: Diameter difference between the middle and tapered top (e.g., 8mm, subtracted from the middle).
*  `$halter_durchmesser_verjuengung`: Diameter of the tapered top (calculated from middle diameter and difference).
*   `$halter_hoehe_mittelstueck`: Height of the middle section (e.g., 20mm).
*   `$halter_hoehe_basis`: Height of the base (e.g., 10mm).
*   `$halter_hoehe_verjuengung`: Height of the tapered top section (calculated from the diameter difference divided by 2 to create a 45-degree angle)
*   `$aussenring_dia`: Diameter of the outer ring (e.g., 90mm).
*   `$aussenring_hoehe`: Height of the main part of the outer ring (e.g., 10mm).
*   `$aussenring_hoehe_stuetzen`: Height of the outer ring's support walls (should be taller than the teabag holder sections) (e.g., sum of the other heights)
*   `$aussenring_ausschnitt_factor`: Percentage for the outer ring support wall cutout size (e.g., 96%).
*    `$aussenring_ausschnitt_cube_y`: Y dimension of the outer ring support wall cutout (calculated from ring diameter and wall thickness).
*    `$aussenring_ausschnitt_cube_x`: X dimension of the outer ring support wall cutout (calculated from the Y dimension and percentage factor).
*   `$aussenring_hoehe_kranz`: Height difference between the top of the outer ring's support walls and the base of the handles (e.g., 5mm).
*   `$griff_laenge`: Length between the two ends of the handles (e.g., 140mm).
*   `$griff_breite`: Width of the handles (e.g., 30mm).
*   `$griff_dicke`: Thickness/depth of the handles (e.g., 4mm).
*   `$griff_hoehe`: Height of the handles (e.g. from the base of the ring to the same level as the support walls - the support height minus the ring height)
*   `$beutelhalter_ausschnitt`: Width of the slot/gap in the middle section (e.g., 19mm).

**A few key relationships to keep in mind:**

*   The `$halter_durchmesser_basis` is `$halter_durchmesser_mittelstueck + $halter_durchmesser_differenz_basis`.
*  The `$halter_durchmesser_verjuengung` is `$halter_durchmesser_mittelstueck - $halter_durchmesser_differenz_verjuengung`.
*   `$halter_hoehe_verjuengung` should calculate to make a 45 degree transition - `($halter_durchmesser_mittelstueck - $halter_durchmesser_verjuengung)/2`
*   `$aussenring_hoehe_stuetzen` should be the sum of the teabag holder heights (`$halter_hoehe_basis` + `$halter_hoehe_mittelstueck` + `$halter_hoehe_verjuengung`).
*    `$griff_hoehe` needs to make a "kranz" at the top of the ring, so set it up so that `aussenring_hoehe_stuetzen - $aussenring_hoehe_kranz`
*  The outer ring cutouts should remove part of the outer ring walls, so should extend from `$aussenring_hoehe` to `$aussenring_hoehe_stuetzen`

I've also got a specific hook design I want to use on the middle section - small "L" hooks on each side of the slit, for six hooks in total, positioned at 45-degree intervals.

Can you generate an OpenSCAD file or files that can make this? I'd like to be able to control all of those parameters with the customizer, and I'd like to be able to adjust the whole thing pretty easily. I'm pretty sure that's all the information you'll need. Let me know if anything is unclear! Also, please don't assume that any extra libraries have been attached. If you use a library, please let me know which one.

[Assistant]
TeeBeutelHalter_raised.scad:
```
// -----------------------------------------------------
// TeeBeutelHalter
//
//  Detlev Ahlgrimm, 10.2018
// -----------------------------------------------------

$fn=100;

// Allgemeine Wanddicke
$wanddicke= 1.4;
// Wanddicke der Außenwand
$aussenring_wanddicke=1.5;

//$halter_durchmesser_mittelstueck=61;    // Halter Durchmesser gerade Wand mit Zähnen
// Teebeutelbreite innenhalb der Pressung ca 82 -> *2 /pi =52.2 Durchmesser;
// Halter Durchmesser gerade Wand mit Zähnen - (Mittelstück-Durchmesser 61 - Teebeutel-Durchmesser ca 52,2) muss < Halter-Ausschnitt (19) sein! Std-Diff = 10
$halter_durchmesser_mittelstueck=56;
// Halter Durchmesser Differenz Basis (breiter als Mittelstück)
$halter_durchmesser_differenz_basis = 9;
$halter_durchmesser_basis=$halter_durchmesser_mittelstueck + $halter_durchmesser_differenz_basis;
// Halter Durchmesser Differenz Verjüngung (schmaler als Mittelstück)
$halter_durchmesser_differenz_verjuengung = 8;
$halter_durchmesser_verjuengung=$halter_durchmesser_mittelstueck - $halter_durchmesser_differenz_verjuengung;  // initial -16

// Halter-Höhe Mittelstück
$halter_hoehe_mittelstueck=20;
// Halter-Höhe breitere Basis
$halter_hoehe_basis=10;
// Halter-Höhe schmalere Verjüngung
$halter_hoehe_verjuengung=($halter_durchmesser_mittelstueck - $halter_durchmesser_verjuengung)/2;   // 45° für Übergang Mittelstück zu Verjüngung

// Außenring-Durchmesser
$aussenring_dia=90;
// Außenring-Höhe (Ring an dem der Halter befestigt ist)
$aussenring_hoehe=10;
// Außenring-Stützen-Höhe (höhere Wände)
//$aussenring_hoehe_stuetzen=$aussenring_hoehe+28;
$aussenring_hoehe_stuetzen=$halter_hoehe_basis + $halter_hoehe_mittelstueck + $halter_hoehe_verjuengung;
// Faktor Aussenring-Ausschnitt (der Außenring-Stützen zum Ring; in %)
$aussenring_ausschnitt_factor=96;
$aussenring_ausschnitt_cube_y=($aussenring_dia + $aussenring_wanddicke*2);
$aussenring_ausschnitt_cube_x= $aussenring_ausschnitt_cube_y  *$aussenring_ausschnitt_factor /100;   // etwas kleiner, damit Rand entsteht

// Kranz-Höhe / Differenz zwischen Außenring-Stützen (höhere Wände) und Griffen
$aussenring_hoehe_kranz=5;
// Entfernung zwischen beiden Griff-Enden
$griff_laenge=140;
$griff_breite=30;
// Dicke / Tiefe des Griffs
$griff_dicke=4;
$griff_hoehe=$aussenring_hoehe_stuetzen - $aussenring_hoehe_kranz;   // tiefer ansetzen, damit aus dem Außenring ein Kranz wird.

// Schlitz zwischen den Backen des Beutelhalters
$beutelhalter_ausschnitt=19;    // orig 19

// -----------------------------------------------------
//   r : Radius (innen)
//   w : Wandstaerke
//   h : Hoehe Rand breit
//   h2 : Hoehe Rand schmal
// -----------------------------------------------------
module Rohr(r, w, h) {
  difference() {
    cylinder(r=r+w, h=h);
    translate([0, 0, -0.1]) cylinder(r=r, h=h+0.2);
  }
}

// -----------------------------------------------------
// dia1 = Halter Durchmesser mit Zähnen (=61)
// dia0 = Halter Durchmesser breite Basis (dia1 + 9=70)
// dia2 = Halter Durchmesser Verjüngung (dia1 - 16=45)
// hgt0 = Höhe Halter breite Basis (=10)
// hgt1 = Höhe Halter mit Zähnen (=20)
// hgt2 = Höhe Halter Verjüngung (=(61-45)/2)
// -----------------------------------------------------
module BeutelHalter(hgt0=$halter_hoehe_basis, hgt1=$halter_hoehe_mittelstueck, wt=1.4, ob=$beutelhalter_ausschnitt) {
  dia0=$halter_durchmesser_basis;
  dia1=$halter_durchmesser_mittelstueck;
  dia2=$halter_durchmesser_verjuengung;
  hgt2=$halter_hoehe_verjuengung;   // 45°
  difference() {
    union() {
      cylinder(d1=dia0, d2=dia1, h=hgt0);
      translate([0, 0, hgt0-0.01]) cylinder(d=dia1, h=hgt1);
      translate([0, 0, hgt0+hgt1-0.01]) cylinder(d1=dia1, d2=dia2, h=hgt2);
      translate([-15, -dia1, 0]) cube([30, 2*dia1, wt]);
      translate([-15, -dia1, 0]) cube([wt, 2*dia1, hgt0]);
      translate([15-wt, -dia1, 0]) cube([wt, 2*dia1, hgt0]);
    }
    translate([0, 0, -0.01]) cylinder(d1=dia0-2*wt, d2=dia1-2*wt, h=hgt0+0.02);
    translate([0, 0, hgt0-0.01]) cylinder(d=dia1-2*wt, h=hgt1+0.02);
    translate([0, 0, hgt0+hgt1-0.01]) cylinder(d1=dia1-2*wt, d2=dia2-2*wt, h=hgt2+0.02);
    // Mittel-Ausschnitt (ob = Ausschnittbreite)
    translate([-dia1, -ob/2, -0.1]) cube([2*dia1, ob, hgt0+hgt1+hgt2+0.2]);
  }
  // Haken
  for(a=[45, 90, 135, -45, -90, -135]) {
    rotate([0, 0, a]) translate([dia1/2-wt/2, -1, hgt0+hgt1/2]) rotate([90, 0, 0]) linear_extrude(2) polygon([[0,0], [0,-3], [3,0], [0,3]]);
  }
}
//!BeutelHalter();

// -----------------------------------------------------
// dia1 = Durchmesser Außenring
// -----------------------------------------------------
module BeutelHalterMitRing(dia1=$aussenring_dia, dia2=40, hgt1=10, hgt2=$aussenring_hoehe, wt=$wanddicke) {
  intersection() {
    difference() {
      union() {
        BeutelHalter();
        difference() {
          // Differenz aussenring_hoehe zu aussenring_hoehe_stuetzen frei machen
          Rohr(dia1/2, $aussenring_wanddicke, $aussenring_hoehe_stuetzen);
          translate( [$aussenring_ausschnitt_cube_x  /2*-1 , $aussenring_ausschnitt_cube_y /2*-1, $aussenring_hoehe])
          cube( [ $aussenring_ausschnitt_cube_x,  $aussenring_ausschnitt_cube_y , $aussenring_hoehe_stuetzen ] );
       }
        translate([0, -dia1/2-dia2/4, 0]) cylinder(r=dia2/2, h=hgt1);
        translate([0, dia1/2+dia2/4, 0]) cylinder(r=dia2/2, h=hgt1);
      }
      translate([0, -dia1/2-dia2/4, -0.01]) cylinder(r=dia2/2-2*wt, h=hgt1+0.2);
      translate([0, dia1/2+dia2/4, -0.01]) cylinder(r=dia2/2-2*wt, h=hgt1+0.2);
    }
    cylinder(r=dia1/2+$aussenring_wanddicke, h=100);
  }
}
//!BeutelHalterMitRing();

// -----------------------------------------------------
//
// -----------------------------------------------------
module BeutelHalterMitRingUndAuslegern(dia1=$griff_laenge, dia2=$griff_breite, hgt1=$aussenring_hoehe_stuetzen, wt=1.4) {
  ausleger_h_bridges=20;    // brückenhöhe

  // Teekanne r=27,5 ; Teetasse r=55 ; normale Tasse 36-40

  ausleger_h_min=0;
  ausleger_h_max=$griff_hoehe - $griff_dicke;
  ausleger_h_stop=ausleger_h_max - ausleger_h_bridges;
  ausleger_h_infill=2;
  ausleger_r_innen=($aussenring_dia + $aussenring_wanddicke*2)/2;   // Tassendurchmesser prüfen
  ausleger_r_mitte=ausleger_r_innen+9;
  ausleger_r_aussen=(dia1 + dia2)/2;

  ausleger_innen_h_min=0;
  ausleger_innen_h_max=$griff_hoehe; // +10 *0.85 +7;
  ausleger_innen_h_stop=ausleger_innen_h_max - ausleger_h_bridges - $griff_dicke;
  ausleger_innen_h_infill=2;
  ausleger_innen_r_innen=35;    // Tassendurchmesser prüfen
  ausleger_innen_r_mitte=ausleger_r_innen-5;
//  ausleger_innen_r_aussen=dia1 /3 -wt -0.3;
  ausleger_innen_r_aussen=$aussenring_dia/2;     // Tassendurchmesser prüfen

  BeutelHalterMitRing();
  difference() {
    difference() {
      hull() {
        translate([-dia1/2, 0, 0]) cylinder(r=dia2/2, h=$griff_hoehe);
        translate([dia1/2, 0, 0]) cylinder(r=dia2/2, h=$griff_hoehe);
      }
      hull() {
        // komplett ausgehöhlt
//        translate([-dia1/2, 0, -0.1]) cylinder(r=dia2/2-wt, h=$aussenring_hoehe_stuetzen+0.2);
//        translate([dia1/2, 0, -0.1]) cylinder(r=dia2/2-wt, h=$aussenring_hoehe_stuetzen+0.2);
        // teil gefüllt
        translate([-dia1/2, 0, ausleger_h_infill * -1]) cylinder(r=dia2/2-wt, h=$griff_hoehe+0.2);
        translate([dia1/2, 0, ausleger_h_infill * -1]) cylinder(r=dia2/2-wt, h=$griff_hoehe+0.2);
      }
      // Innen-Ausschnitt für Halter
      translate([0, 0, -0.1]) cylinder(d=ausleger_innen_r_innen *2 +.04, h=$griff_hoehe+0.2);
    }

//  }
    // Schräg-Stützen außen
    translate([0, 0, -0.1])
    rotate_extrude($fn=200)
    polygon(points=[[ausleger_r_innen,ausleger_h_min],[ausleger_r_innen,ausleger_h_stop],[ausleger_r_mitte,ausleger_h_max],[ausleger_r_aussen,ausleger_h_max],[ausleger_r_aussen,ausleger_h_min]]);

//  }
    // Schräg-Stützen innen
    translate([0, 0, -0.1])
    rotate_extrude($fn=200)
    polygon(points=[[ausleger_innen_r_innen,ausleger_innen_h_min],[ausleger_innen_r_innen,ausleger_innen_h_max],[ausleger_innen_r_innen,ausleger_innen_h_max],[ausleger_innen_r_aussen,ausleger_innen_h_stop],[ausleger_innen_r_aussen,ausleger_innen_h_min]]);
  }
}
BeutelHalterMitRingUndAuslegern();

```


[User]
Okay, I'm looking to design a parametric power strip holder that can attach to a table leg. I've got two slightly different versions of the idea, and I'd like your help generating the OpenSCAD files for both. It should be adaptable to different power strip sizes, table leg sizes, and even cable configurations.

Here's a breakdown of the core requirements, and differences between the two, which should each be in their own file.

**Common Parameters (for both files):**

*   **Wall Thickness:** A parameter `wall_width` (default: 3mm), for the thickness of all printed walls.
*   **Support Length:** A parameter `support_length` (default: 40mm), for the length of the support that extends from the table leg.
*   **Screw/Nut Dimensions:**
    *   `screw_hole_diam` (default: 5.8mm), diameter of the hole for the screw.
    *   `screw_head_diam` (default: 9.9mm), diameter of the screw head.
    *   `nut_diam` (default: 11.1mm), diameter of the nut's circumscribed circle.
*   **Power Strip Dimensions:**
    *   `ps_height` (default: 40mm), height of the power strip.
    *   `ps_width`, the width of the power strip. Note that this is 54.5mm in the `power_outlet_strip_v2.scad`, and 53mm in `power_outlet_strip.scad`. This value should be a parameter of the design.
*   **Cable Hole:**
    *   `cable_hole` (boolean, either true or false). Determines whether to include a cable hole, and if true, takes the following parameters into account:
    *   `cable_diameter` (diameter of the cable). Note that this is 26.5mm in the `power_outlet_strip_v2.scad`, and 12.1mm in `power_outlet_strip.scad`. This value should be a parameter of the design.
    *   `cable_position` (2D vector, default [0,0]), the position of the cable hole.
*   **Borders:**
    *   `border_side` (default: 4mm in `power_outlet_strip_v2.scad`, 3mm in `power_outlet_strip.scad`), border from the sides up to the power outlet slot.
    *   `border_end` (default: 4mm in `power_outlet_strip_v2.scad`, 3mm in `power_outlet_strip.scad`), border from the end of the power strip to the first outlet.
*   **Leg Dimensions:**
    *   `leg_size` (2D vector), dimensions of the table leg. Note that this is [30.5, 42.5] in `power_outlet_strip_v2.scad`, and [43, 65.8] in `power_outlet_strip.scad`. This value should be a parameter of the design.
*   **Printer Tolerance:** `tolerance` (default: 0.5mm).
*   **$fn resolution:** `fn` (default: 150).

**Specific differences:**

1.  **`power_outlet_strip_v2.scad`:**
    *   Includes a boolean `invert_nut_screw` parameter. If `true`, the nut is placed on the top part of the clip and the screw is placed on the bottom, and vice versa if `false`. The nut is assumed to have 6 sides in the support when `invert_nut_screw` is true, and the screw when `invert_nut_screw` is false.
    *   Has `ps_width = 54.5`, `cable_diameter=26.5`, `border_side = 4`, `border_end = 4`, and `leg_size = [30.5, 42.5]`.
2.  **`power_outlet_strip.scad`:**
    *   Does _not_ include an `invert_nut_screw` parameter. The nut is _always_ on the top part of the clip. The nut is assumed to have 6 sides in the support.
    *   Has `ps_width = 53`, `cable_diameter=12.1`, `border_side = 3`, `border_end = 3`, and `leg_size = [43, 65.8]`.
    *   Has `cable_hole = false`.

**Overall Design Requirements:**

*   The design should create a clip-like structure to hold the power strip.
*   The power strip is held in place with a rectangular cutout that corresponds to the power strip dimensions.
*   The holder should be mounted onto a table leg using screws and nuts.
*   The design should use `filleted_rectangle()` for rounded corners.
*   The screw/nut attachments are created using `screw_nut_support`.
*   The `table_support` module creates the table leg attachment.
*   The overall holder is created in the `support` module.
*   The designs can be mirrored to produce the other side of the mount - that doesn't need to be accounted for.

I don't expect you to include any external libraries, and just want these two `.scad` files generated.

[Assistant]
power_outlet_strip_v2.scad:
```

/* [Support Info] */

// Width of printed wall (mm)
wall_width = 3; // 0.1

// Support length (mm)
support_length = 40;

// Diameter of the hole (metric?) (mm)
screw_hole_diam = 5.8;

// Diameter of the screw head (mm)
screw_head_diam = 9.9;

// Nut circunscribed circunference diameter (mm)
nut_diam = 11.1;

/* [Power strip Info] */

// Power strip height
ps_height = 40; // 0.1

// Power strip width (mm)
ps_width = 54.5; // 0.1

// Whether to perform hole for the cable or not
cable_hole = true;

// Diameter of the cable (set to 0 if no cable hole is needed) (mm)
cable_diameter = 26.5;

// Position of the cable hole in the object. Note that 0,0 means centered (mm,mm)
cable_position = [0,0];

// Border on each side up to the power outlet slot (mm)
border_side = 4;

// Border on the end of the power strip to the first power outlet slot (mm)
border_end = 4;

// This flag lets you change in which piece is the nut and screw holes. If the support is smaller you might not be able to introduce the screw, just switch it :)
invert_nut_screw = false;

/* [Leg Info] */
// Leg dimensions, take into account to add rubbers here to aboid slippery (mm,mm)
leg_size = [30.5, 42.5];

/* [Printer settings] */
// Printer tolerance. Printers are not perfect, pieces need a bit of margin to fit. (mm)
tolerance = 0.5;

/* [Other] */
// $fn resolution
fn = 150;

module filleted_rectangle(size_x,size_y,size_z, round_rad)
{
    hull()
    {
        linear_extrude(height = size_z) translate([round_rad,round_rad,0]) circle(d= round_rad*2, $fn = fn);
        linear_extrude(height = size_z) translate([size_x-round_rad,round_rad,0]) circle(d= round_rad*2, $fn = fn);
        linear_extrude(height = size_z) translate([round_rad,size_y-round_rad,0]) circle(d= round_rad*2, $fn = fn);
        linear_extrude(height = size_z) translate([size_x-round_rad,size_y-round_rad,0]) circle(d= round_rad*2, $fn = fn);
    }
}

// sn -> screw/nut dimension
module screw_nut_support(side, h, sn_diam, hole_metric, fn_input)
{
    rotate([0,90,0])
    translate([-side/2,side/2,0])
    difference()
    {
        // Center in X and Y for easyer with cylinders
        translate([-side/2,-side/2,0])
        //cube([side, side, h]);
        filleted_rectangle(side,side,h, wall_width/2);

        translate([0,0,wall_width])
        cylinder(d = sn_diam+tolerance*2, h = h, $fn = fn_input);

        translate([0,0,-0.1])
        cylinder(d = hole_metric+tolerance, h = h+0.2, $fn = fn);
    }
}

module table_support()
{
    length = max(screw_head_diam, nut_diam) + wall_width*2 + tolerance*2;
    size_x = leg_size.x + wall_width*2 + tolerance*2;
    size_y = leg_size.y + wall_width*2 + tolerance*2;

    cut_width = 2;
    // Add screw/nut support
    rotate([0,90,0])
    translate([-wall_width,-size_y/2,0])
    union()
    {
        difference()
        {
            filleted_rectangle(size_x, size_y, length, wall_width/2);

            translate([wall_width, size_y/2 - leg_size.y/2, -0.1])
            cube([leg_size.x,leg_size.y,length+0.2]);

            // Make cut between both parts of clip
            translate([size_x/2 - cut_width/2, -0.1, -0.1])
            cube([cut_width, size_y+0.2, length + 0.2]);
        }

        // Adds lines to invert position of nut and screw on flag
        upper_diam = (invert_nut_screw) ? nut_diam : screw_head_diam;
        lower_diam = (invert_nut_screw) ? screw_head_diam : nut_diam;

        n_h = wall_width + nut_diam/2;
        s_h = wall_width + screw_head_diam/2;

        upper_h = (invert_nut_screw) ? n_h : s_h;
        lower_h = (invert_nut_screw) ? s_h : n_h;

        upper_fn = (invert_nut_screw) ? 6 : fn;
        lower_fn =(invert_nut_screw) ? fn : 6;

        n_side = nut_diam + wall_width*2 + tolerance*2;
        translate([size_x/2+cut_width/2, size_y-wall_width, 0])
        screw_nut_support(n_side, upper_h, upper_diam, screw_hole_diam, upper_fn);

        translate([size_x/2+cut_width/2,wall_width-n_side,0])
        screw_nut_support(n_side, upper_h, upper_diam, screw_hole_diam, upper_fn);

        // Uses nut computed side as nut is bigger thatn screw of same metric:
        // s_side = screw_head_diam + wall_width*2 + tolerance*2;
        translate([size_x/2-cut_width/2, size_y-wall_width, n_side])
        rotate([0,180,0])
        screw_nut_support(n_side, lower_h, lower_diam, screw_hole_diam, lower_fn);

        translate([size_x/2-cut_width/2, wall_width-n_side, n_side])
        rotate([0,180,0])
        screw_nut_support(n_side, lower_h, lower_diam, screw_hole_diam, lower_fn);
    }
}

module support()
{
    size_x = support_length + wall_width*2 + tolerance*2;
    size_y = ps_width + wall_width*2 + tolerance*2;
    size_z = ps_height + wall_width*2 + tolerance*2;

    // Center in Y for easyer handling
    difference()
    {
        union()
        {
            translate([0,-size_y/2,0])
            difference()
            {
                filleted_rectangle(size_x,size_y,size_z, wall_width/2);
                translate([wall_width, size_y/2 - ps_width/2, size_z/2 - ps_height/2])
                cube([size_x*tolerance*2,ps_width*tolerance*2,ps_height*tolerance*2]); // Use size_x to ensure complete cutting

                hole_width = ps_width-border_side*2;
                translate([wall_width + border_end, size_y/2 - hole_width/2, ps_height])
                filleted_rectangle(size_x, ps_width-border_side*2, ps_height, wall_width/2);
            }
            table_support();
        }
        translate([0,-size_y/2,0])
        if (cable_hole)
        {
            // Cable hole
            cable_hole_y = cable_diameter + tolerance*2;
            cable_hole_z = size_z/2 + cable_position.y + cable_hole_y/2;

            translate([-0.1, size_y/2 - cable_hole_y/2  + cable_position.x, -0.1])
            cube([size_x + 0.2,cable_hole_y, cable_hole_z-cable_hole_y/2]);

            translate([-0.1, size_y/2 + cable_position.x, size_z/2 + cable_position.y])
            rotate([0,90,0])
            cylinder(d = cable_hole_y, h = wall_width + 0.2, $fn = fn);
        }
    }
}

support();
```
power_outlet_strip.scad:
```

/* [Support Info] */

// Width of printed wall (mm)
wall_width = 3; // 0.1

// Support length (mm)
support_length = 40;

// Diameter of the hole (metric?) (mm)
screw_hole_diam = 5.8;

// Diameter of the screw head (mm)
screw_head_diam = 9.9;

// Nut circunscribed circunference diameter (mm)
nut_diam = 11.1;

/* [Power strip Info] */

// Power strip height
ps_height = 40; // 0.1

// Power strip width (mm)
ps_width = 53; // 0.1

// Whether to perform hole for the cable or not
cable_hole = false;

// Diameter of the cable (set to 0 if no cable hole is needed) (mm)
cable_diameter = 12.1;

// Position of the cable hole in the object. Note that 0,0 means centered (mm,mm)
cable_position = [0,0];

// Border on each side up to the power outlet slot (mm)
border_side = 3;

// Border on the end of the power strip to the first power outlet slot (mm)
border_end = 3;

/* [Leg Info] */
// Leg dimensions, take into account to add rubbers here to aboid slippery (mm,mm)
leg_size = [43, 65.8];

/* [Printer settings] */
// Printer tolerance. Printers are not perfect, pieces need a bit of margin to fit. (mm)
tolerance = 0.5;

/* [Other] */
// $fn resolution
fn = 150;

module filleted_rectangle(size_x,size_y,size_z, round_rad)
{
    hull()
    {
        linear_extrude(height = size_z) translate([round_rad,round_rad,0]) circle(d= round_rad*2, $fn = fn);
        linear_extrude(height = size_z) translate([size_x-round_rad,round_rad,0]) circle(d= round_rad*2, $fn = fn);
        linear_extrude(height = size_z) translate([round_rad,size_y-round_rad,0]) circle(d= round_rad*2, $fn = fn);
        linear_extrude(height = size_z) translate([size_x-round_rad,size_y-round_rad,0]) circle(d= round_rad*2, $fn = fn);
    }
}

// sn -> screw/nut dimension
module screw_nut_support(side, h, sn_diam, hole_metric, fn_input)
{
    rotate([0,90,0])
    translate([-side/2,side/2,0])
    difference()
    {
        // Center in X and Y for easyer with cylinders
        translate([-side/2,-side/2,0])
        //cube([side, side, h]);
        filleted_rectangle(side,side,h, wall_width/2);

        translate([0,0,wall_width])
        cylinder(d = sn_diam+tolerance*2, h = h, $fn = fn_input);

        translate([0,0,-0.1])
        cylinder(d = hole_metric+tolerance, h = h+0.2, $fn = fn);
    }
}

module table_support()
{
    length = max(screw_head_diam, nut_diam) + wall_width*2 + tolerance*2;
    size_x = leg_size.x + wall_width*2 + tolerance*2;
    size_y = leg_size.y + wall_width*2 + tolerance*2;

    cut_width = 2;
    rotate([0,90,0])
    translate([-wall_width,-size_y/2,0])
    union()
    {
        difference()
        {
            filleted_rectangle(size_x, size_y, length, wall_width/2);

            translate([wall_width, size_y/2 - leg_size.y/2, -0.1])
            cube([leg_size.x,leg_size.y,length+0.2]);

            // Make cut between both parts of clip
            translate([size_x/2 - cut_width/2, -0.1, -0.1])
            cube([cut_width, size_y+0.2, length + 0.2]);
        }

        // Add screw/nut support
        n_h = wall_width + nut_diam/2;
        n_side = nut_diam + wall_width*2 + tolerance*2;
        translate([size_x/2+cut_width/2, size_y-wall_width, 0])
        screw_nut_support(n_side, n_h, nut_diam, screw_hole_diam, 6);

        translate([size_x/2+cut_width/2,wall_width-n_side,0])
        screw_nut_support(n_side, n_h, nut_diam, screw_hole_diam, 6);

        // Uses nut computed side as nut is bigger thatn screw of same metric:
        // s_side = screw_head_diam + wall_width*2 + tolerance*2;
        s_h = wall_width + screw_head_diam/2;
        translate([size_x/2-cut_width/2, size_y-wall_width, n_side])
        rotate([0,180,0])
        screw_nut_support(n_side, s_h, screw_head_diam, screw_hole_diam, fn);

        translate([size_x/2-cut_width/2, wall_width-n_side, n_side])
        rotate([0,180,0])
        screw_nut_support(n_side, s_h, screw_head_diam, screw_hole_diam, fn);
    }
}

module support()
{
    size_x = support_length + wall_width*2 + tolerance*2;
    size_y = ps_width + wall_width*2 + tolerance*2;
    size_z = ps_height + wall_width*2 + tolerance*2;

    // Center in Y for easyer handling
    difference()
    {
        union()
        {
            translate([0,-size_y/2,0])
            difference()
            {
                filleted_rectangle(size_x,size_y,size_z, wall_width/2);
                translate([wall_width, size_y/2 - ps_width/2, size_z/2 - ps_height/2])
                cube([size_x*tolerance*2,ps_width*tolerance*2,ps_height*tolerance*2]); // Use size_x to ensure complete cutting

                hole_width = ps_width-border_side*2;
                translate([wall_width + border_end, size_y/2 - hole_width/2, ps_height])
                filleted_rectangle(size_x, ps_width-border_side*2, ps_height, wall_width/2);
            }
            table_support();
        }
        translate([0,-size_y/2,0])
        if (cable_hole)
        {
            // Cable hole
            cable_hole_y = cable_diameter + tolerance*2;
            cable_hole_z = size_z/2 + cable_position.y + cable_hole_y/2;

            translate([-0.1, size_y/2 - cable_hole_y/2  + cable_position.x, -0.1])
            cube([size_x + 0.2,cable_hole_y, cable_hole_z-cable_hole_y/2]);

            translate([-0.1, size_y/2 + cable_position.x, size_z/2 + cable_position.y])
            rotate([0,90,0])
            cylinder(d = cable_hole_y, h = wall_width + 0.2, $fn = fn);
        }
    }
}

support();
```


[User]
Okay, I'm looking to create a fin alignment jig generator for model rockets, similar to the one I've been using. The idea is to input parameters and get a custom jig.

Here's the breakdown: I need a way to specify the body tube size (like BT-5, BT-20, BT-50, etc.), the number of fins, and the fin width. I want to choose between common stock fin widths like 1/16 inch or use a manual entry. There also needs to be a tolerance parameter to control how tightly the jig fits, generally between 0.25mm and 0.5mm.

The jig itself should be a cylinder with slots cut out for the fins to fit into. The inner diameter of the cylinder should be based on the outer diameter of the body tube, plus the specified tolerance. The outer cylinder is thicker for structural support. There also should be tabs that extend outwards for clamping the fins. The fins should be positioned at equal angles around the circumference. It might be useful to specify the wall thickness, jig height, triangle height for the fin tab cutout, the tab length for clamping, and a tab offset, but these don't need to be user-specified.

Could you create an OpenSCAD script that can generate this? I don't need any external libraries for it, but you can use modules if that helps with readability.

[Assistant]
fin-alignment-customizer.scad:
```
/*
 * fin-alignment-customizer.scad by Rocketship Games - tjkopena
 * https://www.thingiverse.com/rocketshipgames/designs
 */

/* [Global] */

// Body tube
tube = "BT-50"; // "BT-5", "BT-20", "BT-50", "BT-55", "BT-60", "BT-80"

// Number of fins
n = 3;

// Fin width selection
stock = 1.5875; // [ 0: "Manual", 1.5875: "1/16" ]

// Fin width manual entry; overrides selection
fwd = 0; //

wd = fwd ? fwd : stock;

// Tolerance to allow
tol = 0.25;

/* [Hidden] */

// Thickness of the jig's walls
wall = 3 + 0;

// Height of the jig's walls
height = 25 + 0;

// Height of the triangle joining the jig's arcs
height_tri = 6 + 0;

// Length of the tab for clamping fins
tab_len = 20 + 0;

// Space to clear for glue and easier insertion
tab_offset = 4 + 0;

// Body tube definitions
body_tubes = [ [ "BT-5",  13.2, 13.8 ], // 0.6
               [ "BT-20", 18.0, 18.7 ], // 0.7
               [ "BT-50", 24.1, 24.8 ], // 0.7
               [ "BT-55", 32.6, 33.7 ], // 1.1
               [ "BT-60", 40.5, 41.6 ], // 1.1
               [ "BT-80", 65.7, 66.0 ]  // 0.3?
             ];

module right_triangle(x, y, ht) {
  linear_extrude(height=ht)
    polygon(points=[[0,0],[x,0],[0,y]]);
}

module fin_alignment(bt, fin_width, fins, tolerance) {

  bt_inner = bt[1];
  bt_outer = bt[2];

  echo(str("Fin Alignment Jig: ", bt[0], " [", bt_inner, " id, ", bt_outer, " od], ",
           fin_width, " stock, ", fins, " fins, ", tolerance, " tolerance"));

  r_inner = bt_outer/2+tolerance;
  r_outer = r_inner+wall;

  difference() {
    union() {
      cylinder(r = r_outer, h = height);

      for (c = [1:fins])
        rotate(c*360/fins)
          union() {
          w = fin_width+(tolerance+wall)*2;
          translate([r_inner+tab_offset, -w/2, 0])
            cube(size = [tab_len+wall, w, height]);
          translate([r_inner, 0, 0])
            cylinder(r = tab_offset+wall, h = height);
        }
    }

    union() {
      translate([0, 0, -1])
        cylinder(r = r_inner, h = height+2);

      for (c = [1:fins])
        rotate(c*360/fins)
          union() {
            w = fin_width+tolerance*2;
            difference() {
              translate([0, -w/2, -1])
                cube(size=[r_inner+tab_offset+tab_len+wall+1, w, height+2]);
              translate([r_inner+tab_offset+tab_len+wall+2, (w+2)/2, -2])
                rotate(a=[90,-90,0])
                right_triangle(height_tri+4, height_tri+4, w+2);
            }

            translate([r_inner, 0, -1])
              cylinder(r=tab_offset, h=height+2);
        }
    }
  }

}

k = search([tube], body_tubes);
bt = body_tubes[k[0]];

fin_alignment(bt, wd, n, tol);

```


[User]
I need a parametric OpenSCAD design for a corner template generator, and I'd like it to also have the option to create a rack to hold the templates. I've seen examples that use a minimal amount of plastic, and I like that approach.

The core design should allow me to specify the following:

*   **For the corner template (ruler):**
    *   The radius of the corner arc.
    *   The length of the straight guide edges extending from the arc.
    *   The size (diameter) of the pen I'll be using, to ensure clearance.
    *   The thickness of the base plate of the template
    *   The thickness of the guide walls on the template
    *   The height of the guide walls
*   **For the rack:**
    *   The length and width of the rack base.
    *   The height of the pins the templates will hang on.
    *   Clearance around the rack pegs so templates easily slide on and off

I want to be able to switch between generating just a corner template or generating a template rack.

I’d like the templates to have a small lip around the bottom of the curved area and a second small lip where the hanging rack can hold the template. It looks like the code uses `$fs` and `$fn` for circle smoothness – let’s keep that in the generated code, and leave it to the user to customize.

I'm aiming for something that works well with a 0.4 or 0.5mm nozzle and 0.2 or 0.25mm layer height, so keep tolerances in mind. I think the code provided as an example is already pretty good in that regard so try to recreate that as closely as possible. I don't want to generate the 'fishy' combined version as I want to be able to generate different sizes. Also, I don't need to use any special libraries.

[Assistant]
Corner.scad:
```
// Part to create. Select smallest ruler radius for the rack.
output = 1; //[1: Ruler, 2: Rack]

/* [Ruler] */
// Radius of the corner
radius = 20; //[5:0.1:100]
// Length of the guide
guide  = 20;
// Size of the pen
pen_size        = 1.0; //[0:0.05:10]

/* [Rack] */
// Length of the rack plate
rack_length     = 25;
// Width of the rack plate
rack_width      = 25;
// Length of the rack pin
rack_height     = 15.5;

/* [Extra] */

// Thickness of the base plate
plate_thickness = 2;
// Thickness of guides
guide_thickness = 2;
// Height of guides
guide_height    = 10;
// Clearance of the rack part
rack_clearance  = 0.5;

/*[hidden]*/

include <common.scad>

module ruler() {
   intersection() {
   union() {
   difference() {
      cylinder(
         r = radius,
         h = plate_thickness,
         $fs = 0.5, $fn = 128
      );
      translate([0,0,-0.1])
      cylinder(
         r = radius - guide_thickness,
         h = plate_thickness + 0.2,
         $fs = 0.5, $fn = 128
      );
   }
      cylinder(
         r = guide_thickness,
         h = plate_thickness,
         $fs = 0.5, $fn = 128
      );
   }
      translate([-radius, -radius])
      cube([radius, radius, plate_thickness]);
   }

   module side() {
      translate([-radius-guide_thickness-pen_size/2, 0])
      cube([guide_thickness, guide, guide_height]);

      translate([-radius-guide_thickness, 0])
      cube([guide_thickness, guide, plate_thickness]);

      rotate([0, 0, atan2(radius, guide)])
      translate([-guide_thickness, 0])
      cube([
         guide_thickness,
         pow(pow(guide,2)+pow(radius,2), 0.5),
         plate_thickness
      ]);

      translate([-radius, 0])
      cube([
         radius+guide_thickness,
         guide_thickness,
         plate_thickness
      ]);

   }

   side();

   mirror([1,0,0])
   rotate([0,0,90])
   side();
}

module rack() {

   translate([0, radius/2-(rack_length-radius)/3])
   rotate([0,0,45])
   intersection() {
   difference() {
      union() {
         cylinder(
            r = radius - guide_thickness - rack_clearance,
            h = rack_height+plate_thickness,
            $fs = 0.5, $fn = 128
         );

         translate([0,0,rack_height+plate_thickness])
         cylinder(
            r1 = radius - guide_thickness - rack_clearance,
            r2 = radius - guide_thickness - rack_clearance/3,
            h = 2*rack_clearance/3,
            $fs = 0.5, $fn = 128
         );

         translate([0,0,rack_height+plate_thickness+2*rack_clearance/3])
         cylinder(
            r = radius - guide_thickness - rack_clearance/3,
            h = rack_clearance/3,
            $fs = 0.5, $fn = 128
         );
      }
      translate([-guide_thickness/2.5,-guide_thickness/2.5,-0.01])
      #cylinder(
         r = radius - guide_thickness * 2.5 - rack_clearance,
         h = rack_height + plate_thickness+rack_clearance + 0.02,
         $fs = 0.5, $fn = 128
      );
   }
      translate([-radius, -radius])
      cube([
         radius - rack_clearance,
         radius - rack_clearance,
         rack_height+plate_thickness+rack_clearance
      ]);
   }

   translate([0,0,-0.1])
   translate([-rack_width/2, -rack_length/2])
   rcube([rack_width, rack_length, plate_thickness], r=5, tight=true);

}

if(output == 1)
   ruler();
else
   rack();

```


[User]
Hey, I'm looking to create a parametric serial plate generator for Voron 3D printers using OpenSCAD. I've got some basic functionality down, but it's really limited and not very elegant, and I need to make some improvements and make it much more flexible and robust.

Currently, my design uses a couple of STL files for the base plate (one with a logo and one without), and then adds the serial number text via linear extrusion. It works, but it's clunky and inflexible, especially with longer serial numbers.

Here are the specific requirements I'd like to have in a new implementation:

1.  **Parametric Base Plate:** Instead of relying on pre-made STLs for the base plate, I'd love to define the base plate parametrically in OpenSCAD. This way, I can easily adjust the size and potentially the mounting features (like the rear clip), so that I can have different clip sizes for mounting on the back and sides, or different printer models. It should probably start as a rectangular base with the option for rounded corners, and also parameters for things like height, depth, and the mounting features I described.
2.  **Flexible Serial Number:** The serial number text needs to be much more flexible. It needs to handle longer serial numbers without any issues and adapt the text size to fit the plate if needed. It should probably be centered, and should have the ability to adjust the `letter spacing` parametrically. The ability to change the `font` would also be super nice. We can start with the "Play" font, but it would be great to allow it to be changeable from the customizer.
3.  **Logo Option:** The Voron logo should still be an option. Ideally, I don't want to rely on a second STL for that. Instead, the logo should also be parametric, either using a 2D SVG file to create a 3D extrusion, or if that's too difficult a set of parametric shapes to create the Voron logo. The logo should be able to be toggled on or off using a parameter, and be scaled up/down in the x/y plane.
4.  **Customizable Mounting:** As mentioned, the mounting should be fully customizable. I want to be able to define the clip/mounting feature (or even multiple mounting features) parametrically rather than relying on pre-made STLs. So, think about the basic shapes and how I could define their location, size, and shape on the back of the plate. That may include some curved clip elements, which I'd need to understand how to generate in OpenSCAD.
5.  **Customizer Support:** The design needs to work well with the OpenSCAD customizer, with parameters for all the aspects mentioned above: base size, corner radius, plate height and depth, text string, font choice, text spacing, text extrusion depth, logo enable, logo scale, and mount type/size. It'd be good to have some default values for each parameter and some constraints, like the text size not being able to exceed the size of the plate.

It would be great if this was structured well and easy to understand so I can modify it in the future if needed. It also needs to be organized in such a way that it is easy to import different libraries that might make the design easier.

I think that covers it. Let me know if you have any questions. I'm eager to see what you come up with!

[Assistant]
Voron_Logo_Plate.scad:
```
font1="Play";
serial="VX.XXX";
logo=true;

if ( logo ) {
    difference(font1, serial, logo) {
    rotate([0,180,0]) translate([-41.5,0,-20]) import("Serial_Plate_Voron_Logo.stl", center=true);
    rotate([90,0,0]) translate([0,6,-1]) linear_extrude(20) text(text=serial, font=font1, size=8, halign="center");
}
}
else{
    difference(font1, serial, logo) {
    rotate([0,180,0]) translate([-41.5,0,-20]) import("Serial_Plate_Voron_NoLogo.stl", center=true);
    rotate([90,0,0]) translate([0,6,-1]) linear_extrude(20) text(text=serial, font=font1, size=8, halign="center");
}
}
```


[User]
Hey, I'm looking to get a customizable bracket designed for mounting boxes under a desk. I've got some specific parameters in mind that I'd like to be adjustable.

The bracket will essentially be a rectangular U-shape, with two mounting "ears" extending out on either side at the top of the U. I need to be able to adjust:

*   **`width`**: The inner width of the bracket where the box sits.
*   **`height`**: The inner height of the bracket, this is how tall the U is.
*   **`depth`**: The depth of the bracket, how far the bracket extends from the mounting surface.
*   **`thickness`**: The overall thickness of the bracket material.
*   **`screw_hole`**: The diameter of the mounting screw holes.
*   **`mount_length`**: The length of the mounting "ears" that stick out the sides.
*   **`lip`**: The height of a lip that extends inwards along the bottom of the U.
*    **`lip_thickness`**: The thickness of this lip.

I'd also like a counterbore for the screw holes on the mounting ears for the screw heads. I think I would also like to include some mounting features for cable ties. These should be small circular mounting points with a hole in the middle.

Could you generate the OpenSCAD code for this, please? I don't have any preference of libraries.

[Assistant]
customizer_bracket.scad:
```

//The inside width of the bracket.
width=93;

//The inside height of the bracket
height=25.6;

//The depth of the bracket
depth=20;

//The thickness of the bracket
thickness=3;

//Screw hole diameter
screw_hole=4;

//The length of the mount pieces
mount_length=15;

//The height of the lip
lip=3;

//The thinkness of the lip
lip_thickness=2;

/* [Hidden] */
$fn = 30;
length=width;

screw_hole_head_lower=14;
screw_hole_head_upper=20;

module boxMCircle(height,radius, extra) {

    difference(){
        cube([radius+extra, radius+extra, height]);
        translate([0,0,-1]) cylinder(height+2,radius,radius);
    }

}

color("Yellow",1)

translate([thickness/2,-length/2-mount_length-thickness,depth/2]) rotate([90,0,90]) difference()
{
    union() {
        translate([0,0,-thinkness/2]) cube([length,depth,thickness],center=true);

        translate([length/2,-depth/2,-thickness/2]) cube([thickness,depth,height+thickness],center=false);
        translate([-length/2-thickness,-depth/2,-thickness/2]) cube([thickness,depth,height+thickness],center=false);

        translate([length/2+thickness,-depth/2,-thickness/2+height]) cube([mount_length,depth,thickness],center=false);
        translate([-length/2-thickness-mount_length,-depth/2,-thickness/2+height]) cube([mount_length,depth,thickness],center=false);

        difference() {
            translate([length/2+thickness,-depth/2,-thickness*1.5+height]) cube([thickness,depth,thickness],center=false);
            translate([length/2+thickness*2,-depth,-thickness*1.5+height]) rotate([-90,0,0]) cylinder(depth*2, thickness, thickness);
        }

        difference() {
           translate([-length/2-thickness*2,-depth/2,-thickness*1.5+height]) cube([thickness,depth,thickness],center=false);
            translate([-length/2-thickness*2,-depth,-thickness*1.5+height]) rotate([-90,0,0]) cylinder(depth*2, thickness, thickness);
        }

        difference(){
            translate([-length/2,-depth/2,thickness/2]) cube([length,lip_thickness,height]);
            translate([-length/2+lip,-depth/2-thickness/2,thickness/2+lip]) cube([length-lip*2,lip_thickness*2,height*2]);
        }

    }

    difference() {
           translate([-length/2-thickness*2,-depth/1.5,-thickness*1.5]) cube([thickness*2,depth*1.5,thickness*2],center=false);
            translate([-length/2,-depth,+thickness*0.5]) rotate([-90,0,0]) cylinder(depth*2, thickness, thickness);
        }

    difference() {
           translate([+length/2,-depth/1.5,-thickness*1.5]) cube([thickness*2,depth*1.5,thickness*2],center=false);
            translate([+length/2,-depth,+thickness*0.5]) rotate([-90,0,0]) cylinder(depth*2, thickness, thickness);
        }

    translate([(length/2+mount_length/2+thickness), 0, 0]) cylinder(height*2,screw_hole/2,screw_hole/2);

    //translate([(length/2+mount_length/2+thickness), 0, height-thickness]) cylinder(thickness,screw_hole_head_upper/2,screw_hole_head_lower/2);

    translate([-(length/2+mount_length/2+thickness), 0, 0]) cylinder(height*2,screw_hole/2,screw_hole/2);

    translate([-(length/2+mount_length/2+thickness), 0, height-thickness]) cylinder(thickness,screw_hole_head/2,screw_hole/2);

    difference() {
            translate([length/2-thickness,-depth/2+lip_thickness,-thickness*0.5+height]) cube([thickness*2,depth*1.1,thickness*2],center=false);
            translate([length/2+thickness*1,-depth,-thickness*0.5+height]) rotate([-90,0,0]) cylinder(depth*2, thickness, thickness);
        }

    difference() {
       translate([-length/2-thickness*1,-depth/2+lip_thickness,-thickness*0.5+height]) cube([thickness*2,depth*1.1,thickness*2],center=false);
        translate([-length/2-thickness*1,-depth,-thickness*0.5+height]) rotate([-90,0,0]) cylinder(depth*2, thickness, thickness);
    }

    translate([(length/2+mount_length+thickness)-2.5, depth/2-2.5, height-thickness]) boxMCircle(10, 2.5, 1);

    translate([(length/2+mount_length+thickness)-2.5, -depth/2+2.5, height-thickness]) rotate([0,0,270]) boxMCircle(10, 2.5, 1);

    translate([(-length/2-mount_length-thickness)+2.5, depth/2-2.5, height-thickness]) rotate([0,0,90]) boxMCircle(10, 2.5, 1);

    translate([(-length/2-mount_length-thickness)+2.5, -depth/2+2.5, height-thickness]) rotate([0,0,180]) boxMCircle(10, 2.5, 1);

}

```


[User]
I'd like to create a garden label, and I'd like it to be parametric so I can easily change the text, size, and other features. I'm imagining something like the attached, but I'd like a main text field and a subtext field below it. The label will also need a stake to stick it into the ground, with a pointed end.

Specifically, I want to be able to control these parameters:

*   **Main Text:** The string of text for the main label (e.g. "Tomato").
*   **Main Text Size:** The size of the main text.
*   **Main Text Thickness:** The thickness of the extruded main text.
*   **Main Text Font:** The font for the main text.
*   **Main Text Vertical Offset:** A vertical offset to adjust the main text's position.
*   **Subtext:** The string of text for the sub-label (e.g., "Early Girl").
*   **Subtext Size:** The size of the subtext.
*   **Subtext Thickness:** The thickness of the extruded subtext.
*   **Subtext Font:** The font for the subtext.
*   **Subtext Vertical Offset:** A vertical offset to adjust the subtext's position.
*   **Stake Length:** The length of the stake portion.
*   **Stake Thickness:** The thickness of the stake.
*   **Stake Height:** The height of the stake (the vertical dimension of the rectangular prism).
*   **Point Length:** The length of the pointed end of the stake.
*  **Tip Size:** The size of the cube used to generate the point of the stake.

The general idea is to have the text extruded linearly and then have a stake section below the text that can be pushed into the dirt. I'm fine with using the `hull` function to make a sloped stake. I would also want to have an option to adjust the vertical offset for the text and subtext.  Could you create a parametric OpenSCAD file for this? Also, please make sure to use the `linear_extrude` and `text` functions for the text portions.

[Assistant]
garden-sign_subtext.scad:
```
// Customizable Garden Plant Label Stake
// by Mark Thompson

text_string = "Plant";
text_size = 12;
text_thickness = 3;
text_font = "Nimbus Sans L:Bold";
text_y_offset_adjust = 0;

subtext_string = "Plant Variety";
subtext_size = 6;
subtext_thickness = 4;
subtext_font = "Nimbus Sans L:Bold";
subtext_y_offset_adjust = -8;

stake_length = 175;
stake_thickness = 5;
stake_height = 5;

point_length = 10;
tip_size = 0.5;

{
    translate([0,text_y_offset_adjust,0])
        linear_extrude(height=text_thickness, convexity=4)
            text(text_string,size=text_size,font=text_font);

    translate([0,subtext_y_offset_adjust,0])
        linear_extrude(height=subtext_thickness, convexity=4)
            text(subtext_string,size=subtext_size,font=subtext_font);

    translate([stake_length/2,-stake_height/2,stake_thickness/2]) {
        difference() {
            hull() {
                cube([stake_length,stake_height,stake_thickness],true);
                translate([stake_length/2+point_length,stake_height/2-tip_size/2,-stake_thickness/2+tip_size/2])
                    cube([tip_size,tip_size,tip_size],true);
            }

            translate([0,-stake_height/2,stake_thickness/2])
                cube([stake_length+point_length*2+0.1,stake_height,stake_thickness],true);
        }
    }
}
```
garden-sign.scad:
```
// Customizable Garden Plant Label Stake
// by Mark Thompson

text_string = "Plant";
text_size = 12;
text_thickness = 3;
text_font = "Nimbus Sans L:Bold";
text_y_offset_adjust = 0;

stake_length = 125;
stake_thickness = 5;
stake_height = 5;

point_length = 10;
tip_size = 0.5;

{
    translate([0,text_y_offset_adjust,0])
        linear_extrude(height=text_thickness, convexity=4)
            text(text_string,size=text_size,font=text_font);

    translate([stake_length/2,-stake_height/2,stake_thickness/2]) {
        difference() {
            hull() {
                cube([stake_length,stake_height,stake_thickness],true);
                translate([stake_length/2+point_length,stake_height/2-tip_size/2,-stake_thickness/2+tip_size/2])
                    cube([tip_size,tip_size,tip_size],true);
            }

            translate([0,-stake_height/2,stake_thickness/2])
                cube([stake_length+point_length*2+0.1,stake_height,stake_thickness],true);
        }
    }
}
```


[User]
Okay, I'm looking to create a parametric design for a fan-style sanding tool, but I want to split it into two halves that clamp together to hold the sandpaper. I've seen a few designs but none that quite fit what I need. I'd like something I can easily adjust with parameters.

Here’s the basic idea:

*   **Core Shape:** The overall tool will be cylindrical, but sliced in half along the cylinder's length.
*   **Clamping Halves:** Each half will have a slightly beveled edge where they meet so when they are placed together, there is a wedge to press down on the sandpaper. This is really important.
*   **Fan Blades/Flaps:** The cylinder will have a number of "flaps" or "blades" extending from the center, creating the fan design. These flaps are what will have the sandpaper attached to them. The base of the flap needs to have space for the sandpaper, so they will be offset from the flat surface.
*   **Screw Hole:** There will be a central screw hole in the middle of each half.
*   **Assembly Ring**: A ring will go around the halves to keep them in alignment, like you saw in the design I gave.
*   **Parameters:** I'd like to be able to control things like:
    *   The outer diameter of the tool.
    *   The diameter of the inner screw hole.
    *   The height/thickness of the tool.
    *   The radius of any rounded edges (where the bevel meets the rest of the part)
    *   The number of fan blades/flaps.
    *   The thickness of the sanding paper plus a tolerance for it.
    *   The pitch/angle of the bevel/wedge between the two halves, so the clamping force is adjustable.
    *   An offset or gap for the sanding paper on each flap.
 *   **Display Options:** The ability to only display certain parts, like just one half, both halves, or just the ring.

*   **Export Option**: The design should have an export mode, which will move parts around so they can all be exported without overlap.

So, I imagine this as a main scad file, that creates the design and I'd like the `rotate_extrude2` module used in this file, if possible to help with preview issues. I do not have any need to improve on existing libraries.

[Assistant]
fan_grinder_03.scad:
```
/* [Tool] */
// Outer Diameter of the fan grinder tool (25 - 60)
Outer_Diameter = 26; // [25:60]
// Diameter of the inner screw hole (1 - 8.6)
Inner_Diameter = 3.4; //[1:0.1:8.6]
// Height / Thicknes of the tool (5 - 30)
Height = 5; //[5:30]
// Radius of rounded Edge (0 - 4)
Radius = 1; //[0:0.5:4]
// count of flaps
Count = 6; //[2:50]
//Thickness of sanding paper plus tolerance
Paper_Space=0.5;  //[0.1:0.1:1.5]
//Pitch to adjust the angle for clamp (5 - 20)
Pitch=10;  //[5:20]
//Offset gab for sanding paper
Offset=2;  //[0:0.25:3]

/* [View] */
Display = "All"; // [All,Bottom Only,Top Only,Top + Bottom,Ring Only]
// Change orientation for export
Export="False"; //[False,True]

/* [Hidden] */
$fn=90;
D=Outer_Diameter;
R=D/2;
d=Inner_Diameter;
r=d/2;
h=Height;
tr=Radius-0.01;    // temporaer
//Verrundungsradius
vr = tr<(h/2) ? tr : h/2-0.01;
//Groesse Absatz Schleifpapier
AbsatzSchleifSpalt=Offset;
// Dicke Papier und Toleranz
p= 0.5 * (Paper_Space>0 ? Paper_Space:0.1) ;

ci=1/2;             // Abstand Inner Ring
ca=1/2;             // Abstand Radius Papier
dr1=4;              // delta Radius 1. Stufe
dr2=4;              // delta Radius 2. Stufe
dri=dr1+dr2;        // delta R innerer Ring
ag=-360/Count/2;    // Winkelabschnitt pro Fahne

// Winkel innerer Teil
a2=-AbsatzSchleifSpalt*360/(PI*(R-dr1))/2;
a1=ag-a2;            // Winkel aeusserer Teil
//Steigung in Grad pro mm Hoehe
Steigung=(5/tan(90-Pitch)*360)/(2*PI*R)/5;
Twist=-Steigung*(h+1);  //Drehung in Bezug auf h
fi=2*PI*(R-dri)*ag/360;    //Laenge Anbindung Fahne
fa=2*PI*(R)*Twist/360;    // Laenge Schraege

// Fehlerauswertung: Wenn Anbindung unter 2,5mm Lange wird es zu instabil
if  (abs(fi)<2.5 && Export=="False")color("Red"){
  translate([0,15,h+10])
    text("To much flaps,",halign="center");
  translate([0,0,h+10])
    text("connection, at inner ring, to thin.",halign="center");
}

//Anzeige je nach Auswahl Display
x= Export=="True" ? 1:0;
if (Display == "Bottom Only" ||
    Display == "Top + Bottom" ||
    Display == "All") {
    y= Display=="Bottom Only" ? 0:1;
    // Nicht Export oder allein,
    //dann 0.01mm hoeher, sieht schoener aus
    translate([0,0,(1-x)*y*.01])
      Part1();
}

if (Display == "Top Only" ||
    Display == "Top + Bottom" ||
    Display == "All") {
    y= Display=="Top Only" ? 0:1;
      // Bei Export drehen und Versatz in X, sonst Y 0.02 hoeher
    translate([y*x*(D+5),0,x*h + (1-x)*y*.02])
    rotate([x*180,0,0])
    Part2();
}

if (Display == "Ring Only" ||
    Display == "All") {
    y= Display=="Ring Only" ? 0:1;
    //Fuer Export oder "Ring Only" Z=0
    translate([-y*x*(D+15),0,sign(x+(1-y))*1])
      Ring();
}

// Module
module Part1(){

difference() {
  color("Chartreuse")
    cylinder(h=h/2,r=R-dri);
  translate([0,0,-0.5])cylinder(h=h+1,r=r);
}

for (i=[0:360/Count:360]){
  rotate([0,0,i]){
    difference(){
      union(){
        //Innerer Absatz
        color(abs(fi)>2.5 ? "Chartreuse":"Red")
          rotate_extrude2(angle=a2)
          translate([R-dri,0,0])
          square([dr2-ca,h]);
        //Aeusserer Absatz
        color("Chartreuse")
          rotate([0,0,a2])
          rotate_extrude2(angle=a1+Twist/2)
          translate([R-dri,0,0])
          union(){
            offset(r=vr)
            offset(delta=-vr)
            square([dri,h]);
            square([vr,h]);
          }
      }
      union()color("Chartreuse"){
        translate([R-dri+ci-1,-p,-0.5])
          cube([dri,p*2,h+1]);
        rotate([0,0,a2])
          translate([R-dr1-ca,-p,-0.5])
          cube([dr1+ca,p*2,h+1]);
        translate([0,0,h/2])
          cylinder(h=h/2+1,r=R-dri+ci);
        linear_extrude(h+1,convexity=10,twist=Twist)
          rotate([0,0,ag+Twist/2])
          translate([R-dri+ci-1,fa,0])
          square([dri+1,-fa]);
      }
    }
  }
}
}
module Part2(){

translate([0,0,h/2])
  difference() {
    color("RoyalBlue")
      cylinder(h=h/2,r=R-dri-ci);
    translate([0,0,-0.5])
      cylinder(h=h+1,r=r);
}

for (i=[0:360/Count:360]){
  rotate([0,0,i]){
    translate([0,0,-0.001])
    difference(){
      union()translate([0,0,0.001]){
        color(abs(fi)>2.5 ? "RoyalBlue":"Red")
          rotate([0,0,a2])
          rotate_extrude2(angle=-ag-a2-Twist/2)
          translate([R-dri-ci,0,0])
          union(){
            offset(r=vr)
            offset(delta=-vr)
            square([dri+ci,h]);
            square([vr,h]);
          }
      }
      union()color("RoyalBlue"){
        rotate_extrude2(angle=a2-1)
          translate([R-dri-ci-1,-0.5,0])
          square([dr2+ca+ci+1,h+1]);
        translate([R-dri-ci-1,-0.1,-0.5])
          cube([dri-dr1+ci+ca+1,p+0.1,h+1]);
        rotate([0,0,a2])
          translate([R-dr1-ca,-p,-0.5])
          cube([dr1+ca,p*2,h+1]);
        translate([0,0,-1])
          cylinder(h=h/2+1,r=R-dri+ci);
        linear_extrude(h+1,convexity=10,twist=Twist)
          rotate([0,0,-ag+Twist/2])
          translate([R-dri+ci-1.3,0,0])
          square([dri+1,-fa]);
      }
    }

  }
}
}
module Ring(){
  difference(){
    translate([0,0,-1])
    cylinder(h+1,r=R+10);
    cylinder(h+2,r=R+2);
    for (i=[0:360/Count:360]){
      rotate([0,0,i+a2])
      translate([R,0,0])
      cube([15,2,h+2],center=false);
    }
  }
  cylinder(h,r=r-0.5);
}

//------------------
//from "thehans" http://forum.openscad.org/template/NamlServlet.jtp?macro=user_nodes&user=1218
// https://www.thingiverse.com/groups/openscad/forums/general/topic:17846#comment-1428480

module rotate_extrude2(angle=360, convexity=2, xsize=h*2+2,yzsize=R+1) {

  module angle_cut(angle=90,extrude_size=100,polygon_size=100) {
    x = polygon_size*cos(angle/2);
    y = polygon_size*sin(angle/2);
    translate([0,0,-extrude_size/2])
      linear_extrude(extrude_size)
        polygon([[0,0],[x,y],[x,polygon_size],[-polygon_size,polygon_size],[-polygon_size,-polygon_size],[x,-polygon_size],[x,-y]]);
  }

  // support for angle parameter in rotate_extrude was added after release 2015.03
  // Thingiverse customizer is still on 2015.03
  angleSupport = (version_num() > 20150399) ? true : false; // Next openscad releases after 2015.03.xx will have support angle parameter
  // Using angle parameter when possible provides huge speed boost, avoids a difference operation

  if (angleSupport) {
      //echo("*** Angular-extrusion support detected. Fast rendering enabled! ***");
    rotate_extrude(angle=angle,convexity=convexity)
      children();
  } else {
    rotate([0,0,angle/2])
      difference()
      {
        rotate_extrude(convexity=convexity) children();
        angle_cut(angle=angle,extrude_size=xsize, polygon_size=yzsize);
      }
  }
}

```


[User]
Hi, I'm looking for an OpenSCAD script to generate different kinds of chains. I need to be able to make straight chains, circular chains, and spiral chains, all with adjustable parameters.

For all chain types, I'd like to be able to specify:

*   `links`: The number of links in the chain. For circular chains, this should be an even number.
*   `l`: The length of an individual link.
*   `d`: The width of a link. This might be slightly wider for spiral and circular chains.
*   `t`: The thickness or diameter of the cross-section of the link.

For the spiral chain, I need to also be able to control:
* `r0`: The initial radius of the spiral.
* `pitch`: How much the radius increases per turn of the spiral.

I'd like the links themselves to have a rounded, almost toroidal cross-section, and be designed to be printable without supports.  I'd also like to avoid any intersection between the links.

Could you provide an OpenSCAD file that handles all of this? I don't need to use any external libraries, I'd prefer to keep it self-contained. I'm hoping this can be done with a modular approach using OpenSCAD modules, with well-named functions for each component. It'd be great if there was a clear way to switch between the chain types.

[Assistant]
chain_updated_deprecated_assign_value.scad:
```
/**************
Chain generator
(http://www.thingiverse.com/thing:28405)

Author: Stefan Langemark

Generates open or closed chains with configurable
dimensions. Print without support.

Open chains (two ends):
straight_chain()
spiral_chain()

Closed chains (no end):
circular_chain()

Common parameters:
l - length of link
d - width of link
t - thickness of material
links - number of links in chain
The length of the chain when stretched is links*(l-2*t)

The spiral and circular chains need a bit wider chains
than the straight chain to make room for the curvature.

***************/

/**************
 Select output here!
***************/

// Shape of chain
type = "straight"; // [straight,circular,spiral]

// Number of links. Needs to be an even number for circular type!
links = 10;

// Length of link
l = 22;

// Width of link (increase to 15 for circular or spiral type)
d = 12;

// Diameter of cross section
t = 3.5;

// Initial radius for spiral type
r0 = 20;

// Increase of radius per turn for spiral type
pitch = 12;

if (type == "straight") straight_chain(links, l,d,t);
if (type == "circular") circular_chain(links, l,d,t);
if (type == "spiral") spiral_chain(links, l,d,t,r0,pitch);

/*
Open chain in a spiral.
Additional parameters:
r0 - initial radius (mm).
pitch - increase of radius per turn (mm/turn).
*/
module spiral_chain(links=30,l=22,d=15,t=3.5,r0=20,pitch=12)
{
	spacing=l/2+1;
	for (i=[0:links-1])
	assign(angle = inv_arch_len(spacing*i,r0,pitch),
		angle2 = inv_arch_len(spacing*(i+1),r0,pitch))
	assign(p = arch_spiral(angle,pitch),
		p2 = arch_spiral(angle2,pitch))
	{
		translate(p)
		rotate([0,0,atan2(p2[1]-p[1],p2[0]-p[0])])
		translate([spacing/2,0,0])
		rotate([60+60*(i%2),0,0])
		link(l,d,t);
	}
}

// Straight chain segment
module straight_chain(links=5, l = 22, d = 12, t = 3.5)
{
	spacing=l/2+.5;
	for (i=[0:links-1]) {
		translate([i*spacing,0,0])rotate([60+60*(i%2),0,0])link(l,d,t);
	}
}

// Closed chain in a circle
module circular_chain(links=14, l = 22, d = 15, t = 3.5)
{
	a = 360/links;
	spacing=l/2+1; // tweak this to make chains with few links
	r=spacing/2/tan(a/2);
	echo("radius",r);
	for (i=[0:links-1]) {
		rotate([0,0,i*a])translate([0,-r,0])rotate([60+60*(i%2),0,0])link(l,d,t);
	}
}

// Single chain link. Centered, with l,d,t in axes x,y,z, respectively.
module link(l,d,t)
{
	union() {
	for (a=[0:7])
	{

                off1 = (l-d)/2*((a-.5>2&&a-.5<6)?-1:1);
                off2 = (l-d)/2*((a+.5>2&&a+.5<6)?-1:1);
                intersection() {
				translate([0,0,-t/2])
				pie((a-.5)*360/8,
					(a+.5)*360/8,l,t,
					[off1,0],[off2,0]);
				translate([(off1+off2)/2,0,0])
				rotate([0,0,a*360/8])
				rotate([-90,0,0])
				translate([(d-t)/2,0,-l/2])
				cylinder($fn=6,r=t/2,h=l);
			}
		}
	}
}

function pol(r,a) = [r*cos(a),r*sin(a)];
module pie(a1,a2,r,h,o1,o2)
{
	if (o1==o2) {
	linear_extrude(height=h)
		polygon([o1,o1+pol(r,a1),o1+pol(r,a2)]);
	} else {
	linear_extrude(height=h)
		polygon([o1,o1+pol(r,a1),o2+pol(r,a2),o2]);
	}
}

// Approximate angle of archimedes spiral of length len (mm) from radius r0, given pitch a (mm/turn).
function inv_arch_len(len,r0,a) = 180/3.1416*sqrt(len*2/(a/2/3.1416)+(r0*2*3.1416/a)*(r0*2*3.1416/a));

// Point on archimedes spiral at angle given pitch a (mm/turn).
function arch_spiral(angle,a) = [a*angle/360*cos(angle),a*angle/360*sin(angle),0];

```


[User]
I'm looking for an OpenSCAD design for a cable clip that can be mounted with a nail. I need it to be parametric so I can adjust it for different cable and nail sizes, and also the material thickness.

Specifically, I need to be able to control these things:

*   **Cable Hole:**
    *   Width of the cable hole
    *   Height of the cable hole
    *   Corner radius of the cable hole (as a percentage of the minimum of the width and height, like the original design)
*   **Nail Hole:**
    *   Diameter of the nail hole
    *   Diameter of the nail head
    *   Countersink angle for the nail head
    *   Outer corner radius for the nail section (again, as a percentage based on the nail diameter plus twice the material thickness)
*   **Other:**
    *   Material thickness of the clip
    *   An option to rotate the clip for easier printing (onto its side or top face)

I'd like the design to use `hull()` to create smooth outer edges. The original design used `intersection()` and `cylinder()` for some of the rounded parts, which seems like a good approach. I'd also like the nail hole to include an option for a countersink so that the nail head can sit flush or below the surface. It would be really helpful if I could pick from a few common countersink angles, like 82 and 90 degrees.

The design should be self-contained; I don't expect any library dependencies, just the core OpenSCAD language.

[Assistant]
codeandmake.com_Cable_Clip_v1.0.scad:
```
/*
 * Copyright 2021 Code and Make (codeandmake.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Cable Clip with Nail Hole by Code and Make (https://codeandmake.com/)
 *
 * https://codeandmake.com/post/fully-customizable-cable-clip
 *
 * Cable Clip with Nail Hole v1.0 (22 February 2021)
 */

/* [Cable] */

// The width of the cable hole
Cable_Hole_Width = 6.3; // [2:0.1:30]

// The height of the cable hole
Cable_Hole_Height = 6.3; // [2:0.1:30]

// The size of the corner radius as a percentage
Cable_Hole_Corner_Radius_Percentage = 100; // [0:1:100]

/* [Nail] */

// The diameter of the nail hole
Nail_Hole_Diameter = 2.2; // [2:0.1:10]

// The diameter of the nail head
Nail_Head_Diameter = 3; // [2:0.1:15]

// Nail countersink angle (82 and 90 are common)
Nail_Head_Countersink_Angle = 90; // [0:0.1:180]

// The size of the outer corner radius as a percentage
Nail_Outer_Corner_Radius_Percentage = 100; // [0:1:100]

/* [Other] */

// The thickness of the material
Material_Thickness = 2; // [2:0.1:20]

// Rotate onto side or top for easier printing
Rotate_For_Printing = 1; // [0:No, 1:Side, 2:Top]

module cableClip() {
  $fn = 100;

  cableHoleCornerRadius = min(Cable_Hole_Width / 2, Cable_Hole_Height / 2) * (Cable_Hole_Corner_Radius_Percentage / 100);
  nailDiameter = max(Nail_Hole_Diameter, Nail_Head_Diameter);
  nailOuterCornerRadius = (nailDiameter + (Material_Thickness * 2)) / 2 * (Nail_Outer_Corner_Radius_Percentage / 100);

  if (Rotate_For_Printing == 1) {
    translate([(nailDiameter + (Material_Thickness * 2)) / 2, Cable_Hole_Height + Material_Thickness, (nailDiameter + (Material_Thickness * 2)) / 2]) {
      rotate([90, 0, 0]) {
        clip();
      }
    }
  } else if (Rotate_For_Printing == 2) {
    translate([(nailDiameter + (Material_Thickness * 2)) / 2, (nailDiameter + (Material_Thickness * 2)) / 2, Cable_Hole_Height + Material_Thickness]) {
      rotate([180, 0, 0]) {
        clip();
      }
    }
  } else {
    translate([(nailDiameter + (Material_Thickness * 2)) / 2, (nailDiameter + (Material_Thickness * 2)) / 2, 0]) {
      clip();
    }
  }

  module clip() {
    translate([0, -(nailDiameter + (Material_Thickness * 2)) / 2, 0]) {
      difference() {
        clipBody();
        cableHole();
        nailHole();
      }
    }
  }

  module clipBody() {
    hull() {
      translate([0, (nailDiameter + (Material_Thickness * 2)) / 2, 0]) {
        nailSection();
      }

      translate([((nailDiameter + (Material_Thickness * 2)) / 2) + Cable_Hole_Width, 0, 0]) {
        cube([Material_Thickness, nailDiameter + (Material_Thickness * 2), Cable_Hole_Height - cableHoleCornerRadius]);
      }

      clipCorner();
    }
  }

  module clipCorner() {
    translate([(nailDiameter / 2) + Material_Thickness + Cable_Hole_Width - cableHoleCornerRadius, 0, Cable_Hole_Height - cableHoleCornerRadius]) {
      intersection() {
        rotate([-90, 0, 0]) {
          cylinder(r = Material_Thickness + cableHoleCornerRadius, h = nailDiameter + (Material_Thickness * 2));
        }

        cube([Material_Thickness + cableHoleCornerRadius, nailDiameter + (Material_Thickness * 2), Material_Thickness + cableHoleCornerRadius]);
      }
    }
  }

  module cableHoleCorner() {
    translate([(Cable_Hole_Width / 2) - cableHoleCornerRadius, -0.5, Cable_Hole_Height - cableHoleCornerRadius]) {
      intersection() {
        rotate([-90, 0, 0]) {
          cylinder(r = cableHoleCornerRadius, h = nailDiameter + (Material_Thickness * 2) + 1);
        }

        cube([cableHoleCornerRadius,  nailDiameter + (Material_Thickness * 2) + 1, cableHoleCornerRadius]);
      }
    }
  }

  module cableHole() {
    translate([(nailDiameter + (Material_Thickness * 2) + Cable_Hole_Width) / 2, 0, 0]) {
      hull() {
        cableHoleCorner();

        mirror([1, 0, 0]) {
          cableHoleCorner();
        }

        translate([-Cable_Hole_Width / 2, -0.5, -0.5]) {
          cube([Cable_Hole_Width, nailDiameter + (Material_Thickness * 2) + 1, 0.5 + (!Cable_Hole_Corner_Radius_Percentage ? Cable_Hole_Height : 0)]);
        }
      }
    }
  }

  module nailSection() {
    if (nailOuterCornerRadius) {
      hull() {
        nailOuterCorner();

        mirror([0, 1, 0]) {
          nailOuterCorner();
        }
      }
    } else {
      translate([-(nailDiameter + (Material_Thickness * 2)) / 2, -(nailDiameter + (Material_Thickness * 2)) / 2, 0]) {
        cube([Material_Thickness, nailDiameter + (Material_Thickness * 2), Cable_Hole_Height + Material_Thickness]);
      }
    }
  }

  module nailOuterCorner() {
    translate([(-(nailDiameter + (Material_Thickness * 2)) / 2) + nailOuterCornerRadius, ((nailDiameter + (Material_Thickness * 2)) / 2) - nailOuterCornerRadius, 0]) {
      intersection() {
        cylinder(r = nailOuterCornerRadius, h = Cable_Hole_Height + Material_Thickness);
        translate([-nailOuterCornerRadius, 0, 0]) {
          cube([nailOuterCornerRadius, nailOuterCornerRadius, Cable_Hole_Height + Material_Thickness]);
        }
      }
    }
  }

  module nailHole() {
    translate([0, (nailDiameter + (Material_Thickness * 2)) / 2, -0.5]) {
      cylinder(d = (Nail_Head_Countersink_Angle ? Nail_Hole_Diameter : nailDiameter), h = Cable_Hole_Height + Material_Thickness + 1);
    }

    if(Nail_Head_Countersink_Angle) {
      countersinkHeight = tan((180 - (Nail_Head_Countersink_Angle)) / 2) * (Nail_Head_Diameter / 2);
      translate([0, (nailDiameter + (Material_Thickness * 2)) / 2, Cable_Hole_Height + Material_Thickness - countersinkHeight]) {
        cylinder(d2 = Nail_Head_Diameter * 2, d1 = 0, h = countersinkHeight * 2);
      }
    }
  }
}

cableClip();

```


[User]
Hey, I'm looking to create a customizable, retro-style cartridge and slot system for housing USB devices, and I'd like it to be parametric so I can adjust dimensions for different devices. I've got some ideas from a project I saw, but I'd like to start from scratch with an OpenSCAD design, and I need the design to be able to generate STL files.

Specifically, I need two main components: a cartridge and a slot/docking station.

**Cartridge:**

*   It should be a mostly rectangular shell, kind of like an old video game cartridge, with a distinct "tail" feature at the bottom.
*   I need to be able to control the overall length of the cartridge and whether or not it has side or bottom grips.
*   It should have an opening on the top for accessing the USB device. I'd like to be able to specify its depth.
*   I want the option to add an antenna opening as well.
*   I also need to have screw holes on the front and rear.
*   It should have internal voids, both for the USB opening and for the main body.

**Slot/Docking Station:**

*   This is where the cartridge will plug into. It should have a receptacle for the cartridge as well as a place to hold the USB extension that connects to the device inside the cartridge.
*   I want to be able to control the depth of the cartridge opening.
*   I need to be able to set the width and height of the USB extension opening.
*  The slot needs mounting lugs to connect to the base, and holes for set screws.
* It should be able to attach to the base with screws as well.

**Key Parameters:**

I'd like to be able to set these parameters via the customizer:

*   `cart_y`: Overall cartridge length.
*   `side_grip`: Enable/disable side grips on the cartridge (boolean).
*   `bottom_grip`: Enable/disable bottom grips on the cartridge (boolean).
*   `antenna_cart`: Enable/disable antenna opening on the cartridge (boolean).
*   `cart_open_y`: Cartridge slot depth in the docking station.
*   `open_x`: Width of the USB extension opening.
*   `open_z`: Height of the USB extension opening.

There are a bunch of internal dimension variables in the provided file too, which are used in calculations, that could be useful to include in the design, like `cart_open_x`, `cart_open_z`, `cart_key_x`, `cart_key_z`, `reader_x`, `reader_y`, `reader_z`, `mount_x`, `mount_y`, `mount_z`, `usb_x`, `usb_y`, `usb_z`, `usb_offset`, `slot_offset`, `grip_offset`, `cart_x`, `cart_z`, `slot_x`, `slot_y`, `slot_z`, `screw_x`, `screw_y`, `screw_dia`, `screw_counter_dia`, `screw_counter_z`, `tail_l`, `tail_tip`, `open_depth`, `open_width`, `open_wall`, `ridge_x`, `ridge_t`, `ridge_z`, `ridge_y`, `usb_notch_x`, and `usb_notch_z`. These should probably remain static, but may be useful to tweak later on.

I also like how the original design is using modules for things like `roundedplate`, `m3_hole`, `m3_nut`, and `mount_lug`, which can be used in the design. It makes things cleaner and more modular. So that would be great to include.

I think that's everything. Let me know if you have any questions, and thanks in advance for the help!

[Assistant]
retrocart.scad:
```
// Customizable version of retroCART Code

/* [Objects] */
// Enable rendering cartridge body
render_cart = true;

// Enable rendering slot body
render_body = false;

// Enable rendering slot rear
render_rear = false;

/* [Cart Options] */
// Cartridge length
cart_y = 65; // [40:100]

// Cartridge side grip
side_grip = true;

// Cartridge bottom grip
bottom_grip = false;

// Enable antenna opening
antenna_cart = false;

/* [Slot Options] */
// Cartridge slot depth
cart_open_y = 20; // [10:50]

// Width of USB extension
open_x = 17.9; // [16:0.1:22]

// Height of USB extension
open_z = 9.7; // [9:0.1:12]

// Variables below are considered static
/*[Hidden]*/
// Increase resolution
$fn=50;

// Cartridge opening
cart_open_x = 72.5;
cart_open_z = 18.6;
cart_key_x = 6.5;
cart_key_z = .8;

// Reader dimensions
reader_x = 78;
reader_y = 25;
reader_z = 3;

// USB extension
mount_x = 50;
mount_y = 13.30;
mount_z = 4;

// USB mount
usb_x = 23;
usb_y = 16;
usb_z = 3;

// How far off the center line to move opening
usb_offset = 0;

// Where to position slot parts
slot_offset = 60;

// Side grip position
grip_offset = 12;

// Standard outside dimensions
cart_x = 72.15;
cart_z = 18;

// Bottom slot
slot_x = 7;
slot_y = 30.5;
slot_z = 1.2;

// Screw positions (measured from front)
screw_x = 9;
screw_y = 10;
screw_dia = 2.5;
screw_counter_dia = 6.5;
screw_counter_z = 4;

// Tail
tail_l = 6;
tail_tip = 10;

// Front opening
open_depth = 15;
open_width = 45;
open_wall = 1.8;

// Bottom ridges
ridge_x = 42;
ridge_t = 1;
ridge_z = 1;
ridge_y = 9;

// USB slot
usb_notch_x = 12.2;
usb_notch_z = 4.6;

// Math
key_pos = -((cart_open_z / 2) - (cart_key_z / 2));
screw_pos = ((cart_open_x / 2) + 6);
opening_x = (cart_x - (open_wall * 2));
opening_y = (cart_y - (open_wall * 2));
opening_z = (cart_z - (open_wall * 2));
opening_y_offset = (((cart_y - tail_l) / 2) - (open_depth / 2.02));
slot_y_offset = (((cart_y - tail_l) / 2) - (slot_y / 2.02));
ridge_start = -((cart_y / 2) - ridge_y);
void_size = ((cart_y - tail_l) - (open_depth + open_wall));
void_start = (((cart_y - tail_l) / 2) - (void_size / 2) - (open_depth + open_wall));
front_screw_y = (((cart_y - tail_l) / 2) - screw_y);
rear_screw_y = -(((cart_y - tail_l) / 2) - 5);

// Slot Modules
module roundedplate(x, y, z, dia)
{
    // Calculate corners
    corner_x = ((x / 2) - (dia / 2));
    corner_y = ((y / 2) - (dia / 2));

    hull()
    {
        translate([corner_x, corner_y, 0])
            cylinder(h=z, d=dia, center=true);

        translate([corner_x, -corner_y, 0])
            cylinder(h=z, d=dia, center=true);

        translate([-corner_x, corner_y, 0])
            cylinder(h=z, d=dia, center=true);

        translate([-corner_x, -corner_y, 0])
            cylinder(h=z, d=dia, center=true);
    }
}

module m3_hole()
{
    cylinder(h=15, d=3.2, center=true);
}

module m3_nut()
{
    translate([0, 0, -1.75])
        cylinder(h=3.5, d=6.35, center=true, $fn=6);
}

module cart_void()
{
    rotate([90, 0, 0])
    {
        difference()
        {
            cube([cart_open_x, (cart_open_y + 0.1), cart_open_z], center=true);
            translate([0, 0, (key_pos - 0.1)])
                cube([cart_key_x, (cart_open_y + 0.2), cart_key_z], center=true);
        }
    }
}

module mount_lug(lug_length)
{
    translate([screw_pos, 0, 0])
    {
        difference()
        {
            hull()
            {
                cylinder(h=lug_length, d=9, center=true);
                translate([-4.9, 0, 0])
                    cube([2, 15, lug_length], center=true);
            }
            cylinder(h=(lug_length + 1), d=2.8, center=true);
        }

    }
}

module mount_lugs(lug_length)
{
    mount_lug(lug_length);
    rotate([0, 0, 180])
        mount_lug(lug_length);
}

module usb_holder()
{
    hull()
    {
    translate([0, ((mount_y / 2) - (usb_z / 2)), 0])
        rotate([90, 0, 0])
            roundedplate(usb_x, usb_y, usb_z, 3);

    translate([0, -((mount_y / 2) - (usb_z / 2)), 0])
        rotate([90, 0, 0])
            roundedplate(usb_x, usb_y, usb_z, 3);
    }
}

module rear_usb_opening()
{
    cube([open_x, 20, open_z], center=true);
}

module set_screw_hump()
{
    hull()
    {
    translate([0, 0, (open_z / 2)])
        cube([(usb_x / 2), mount_y, 1], center=true);

    translate([0, 0, ((reader_y / 2) - .5)])
        cube([(usb_x / 3), mount_y, 1], center=true);
    }
}

module set_screw()
{
    translate([0, 0, ((open_z / 2) + 3)])
    {
        // Hole
        rotate([180, 0, 0])
            translate([0, 0, 1])
                m3_hole();

        // Hex opening
        m3_nut();
    }
}

module usb_mount()
{
    difference()
    {
        union()
        {
            translate([0, 0, usb_offset])
                usb_holder();
            set_screw_hump();
        }

        // Opening for USB cable
        translate([0, 0, usb_offset])
            rear_usb_opening();

        // Hole and nut catch for set screw
        set_screw();
    }
}

module body()
{
    difference()
    {
        roundedplate(reader_x, reader_y, cart_open_y, 3);
        cart_void();
    }

    // Add wings for screws
    mount_lugs(cart_open_y);
}

module rear()
{
    difference()
    {
        union()
        {
            roundedplate(reader_x, reader_y, reader_z, 3);
            mount_lugs(3);
        }

        // Pop USB opening through plate
        cube([open_x, open_z, 5], center=true);

        // Widen screw holes
        translate([screw_pos, 0, 5])
            m3_hole();
        translate([-screw_pos, 0, 5])
            m3_hole();
    }

    // Add USB extension mount
    translate([0, 0, 5.2])
        rotate([90, 0, 0]) usb_mount();
}

// Cart modules
module tail()
{
    hull()
    {
        translate([0, -((cart_y - tail_l) / 2), 0])
            cube([cart_x, 1, cart_z], center=true);

        translate([0, -(((cart_y - tail_l) / 2) + tail_l), 0])
            cube([cart_x, 1, tail_tip], center=true);
    }
}

module slot()
{
    translate([0, slot_y_offset, -((cart_z / 2) - (slot_z / 2.1))])
        cube([slot_x, slot_y, slot_z], center=true);
}

module opening()
{
    translate([0, opening_y_offset, 0])
        cube([open_width, open_depth, opening_z], center=true);
}

module bottom_grip()
{
    for (i=[0:2:16])
    {
        translate([0, (ridge_start + i), -((cart_z / 2) - (ridge_z / 2.2))])
            cube([ridge_x, ridge_t, ridge_z], center=true);
    }
}

module internal_void()
{
    translate([0, void_start, 0])
        cube([opening_x, void_size, opening_z], center=true);
}

module antenna_void()
{
    hull()
    {
        translate([0, -((cart_y / 2) - 2), 0])
            cube([15, 1, 20], center=true);

        translate([0, -((cart_y / 2) + 4), 0])
            cube([30, 1, 20], center=true);
    }

    translate([0, -30, 0])
        rotate([90, 0, 0])
            cylinder(h=40, d=6.5, center=true);
}

module screw_hole()
{
    // Main hole
    translate([0, 0, -2])
        cylinder(h=cart_z, d=screw_dia, center=true);

    // Countersink hole
    translate([0, 0, -((cart_z / 2) - (screw_counter_z / 2))])
        cylinder(h=screw_counter_z, d=screw_counter_dia, center=true);

    // Open up screw hole on bottom side
    translate([0, 0, -4.399]) // Much accurate, very wow
        cylinder(h=(cart_z / 2), d=(screw_dia * 1.25), center=true);
}

module screw_holes()
{
    translate([((cart_x / 2) - screw_x), front_screw_y, -.1])
        screw_hole();

    translate([-((cart_x / 2) - screw_x), front_screw_y, -.1])
        screw_hole();

    translate([((cart_x / 2) - screw_x), rear_screw_y, -.1])
        screw_hole();

    translate([-((cart_x / 2) - screw_x), rear_screw_y, -.1])
        screw_hole();
}

module usb_opening()
{
    translate([0, opening_y_offset, 0])
        cube([usb_notch_x, 20, usb_notch_z], center=true);
}

module rear_blocks()
{
    translate([((cart_x / 2) - 7.5), -((cart_y / 2) - 9), 0])
        cube([14, 12, cart_z], center=true);

    translate([-((cart_x / 2) - 7.5), -((cart_y / 2) - 9), 0])
        cube([14, 12, cart_z], center=true);
}

module alignment_pin(dia, length)
{
    cylinder(h=length, d=dia, center=true);
}

module alignment_pins(dia, length)
{
    translate([((cart_x / 2) - 5), ((cart_y / 2) - 8), 0])
        alignment_pin(dia, length);

    translate([(-(cart_x / 2) + 5), ((cart_y / 2) - 8), 0])
        alignment_pin(dia, length);

    translate([((cart_x / 2) - 5), -((cart_y / 2) - 2), 0])
        alignment_pin(dia, length);

    translate([(-(cart_x / 2) + 5), -((cart_y / 2) - 2), 0])
        alignment_pin(dia, length);
}

module side_grip()
{
    for(i = [0:3.5:16])
            translate([0, i, 0])
                cube([3, 1.5, (cart_z * 1.1)], center=true);
}

module side_grips()
{
    translate([((cart_x / 2) + .7), -((cart_y / 2) - grip_offset), 0])
        side_grip();

    translate([-((cart_x / 2) + .7), -((cart_y / 2) - grip_offset), 0])
        side_grip();
}

module full_cart()
{
    difference()
    {
        union()
        {
            // Open up inside void before putting rear blocks in
            difference()
            {
                cube([cart_x, (cart_y - tail_l), cart_z], center=true);
                internal_void();
            }
            rear_blocks();
        }

        // Internal details
        opening();
        slot();
        usb_opening();
        screw_holes();

        // Grips
        if (bottom_grip)
            bottom_grip();

        if (side_grip)
            side_grips();
    }
    tail();
}

module bottom_half()
{
    difference()
    {
        full_cart();
        translate([0, 0, (cart_z / 2)])
            cube([100, (cart_y + 50), cart_z], center=true);
    }
    alignment_pins(3, 4);
}

module top_half()
{
    rotate([0, 180, 0])
    {
        difference()
        {
            full_cart();
            translate([0, 0, -(cart_z / 2)])
                cube([100, (cart_y + 50), cart_z], center=true);

            translate([0, 0, 1])
                alignment_pins(3.2, 5);
        }
    }
}

module basic_cart()
{
    bottom_half();
    translate([80, 0, 0])
        top_half();
}

// Rendering

if (render_cart)
{
    difference()
    {
        translate([-40, 0, (cart_z / 2)])
            basic_cart();
        if (antenna_cart)
        {
            // Remove from bottom
            translate([-40, 0, (cart_z / 2)])
                antenna_void();

            // Remove from top
            translate([40, 0, (cart_z / 2)])
                antenna_void();
        }
    }
}

if (render_body)
    translate([0, (slot_offset + 50), (cart_open_y / 2)])
        body();

if (render_rear)
    translate([0, slot_offset, (reader_z / 2)])
        rear();

// EOF
```


[User]
I'd like to design a parametric tripod head in OpenSCAD. I've seen a few designs online, but I want something that's easily customizable. I need it to have a base plate that attaches to the tripod, a top plate that connects to a camera, and hinges connecting the two.

Here are the details and parameters I'd like to control:

**Overall Structure:**

*   I need to be able to generate three parts: a demo assembly, just the base plate, or just the top plate.
*   I need to control the smoothness of the parts. Let's say a smoothness variable that sets the `$fn` value, and I'd like to have options for "low," "medium," and "fine" that correspond to $fn values of 48, 60, and 72, respectively.
*   There should be a small gap between parts and a bevel on all edges. I want to be able to set those as parameters.

**Camera Screw:**

*   The camera screw hole needs to be customizable for different screw heads. I want parameters for the screw head diameter, screw head thickness, and the gap between the screw and the hole.

**Base Plate:**

*   I need to be able to control the base plate diameter and thickness.

**Top Plate:**

*   The top plate should have parameters for its width, length, and height.
*   It needs a camera screw hole. I'd also like an option for this to be a slot, instead of a circle, for fine adjustments of camera position.

**Tripod Hinge:**

*   I need to be able to control the length, width, and thickness of the individual hinges.
*   I need to be able to adjust the offset of the hinges from the center of the base plate.
*   The gap between the base plate hinge and the top plate hinge needs to be configurable, as does the number of hinges.

**Important Notes:**

*   I'm not looking for a ball-head tripod. I specifically need hinges.
*   I don't have any attached code, or .scad libraries.

I think that covers it. It seems like a lot, but I'd really appreciate it if you could generate the OpenSCAD files to handle all of that. Let me know if anything is unclear.

[Assistant]
UTripodHead.scad:
```
//// Version: OpenSCAD 2015.03-2
//// Date: 2021.2.10
//// File: TripodHead.scad
//// Author: Kijja Chaovanakij

//// Get additional screw and nut sizes at www.thingiverse.com/thing:4763840

/*[part and option]*/
//part to generate
part=0; //[0:demo, 1:base plate, 2:top plate]
//top plate hole option
slot_hole=1; //[0:no, 1::yes]

/*[smoothness and tolerance]*/
//parts smoothness
smooth=60; //[48:low, 60:medium, 72:fine]
$fn=smooth;
//gap between parts[0.25]
part_gap=0.25;
//parts bevel[0.75]
part_bevel=0.75;

/*[camera screw]*/
//camera screw head diameter[13]
head_dia=13; //[13, 16, 19]
//camera screw head thickness[6]
head_thk=6;
//gap between camera screw and hole[0.3]
screw_hole_gap=0.3;

/*[tripod head base plate]*/
//base plate diameter[15]
base_dia=15;
//base plate thickness[5]
base_hi=5;

/*[tripod head top plate]*/
//top plate width[30]
top_wid=30;
//top plate length[40]
top_leng=40;
//top plate height[5]
top_hi=5;

/*[tripod hinge]*/
//hinge length[13]
hinge_leng=13;
//hinge width[12]
hinge_wid=12;
//hinge thickness[4]
hinge_thk=4;
//Compensate for the distance between hinge center and tripod base plate center. (Usable if bigger than the smallest distance)[15]
hinge_offset=15;
//gap between base plate hinge and top plate hinge.[0.25]
hinge_gap=0.25;
//number of base plate hinge.[3]
n_hinge=3;

/*[Hidden]*/
//tripod_body_hi=15.25; //for base plate screw length[15.25]
screw_dia=5.768; //define for camera screw diameter[5.768]
screw_hole=screw_dia+screw_hole_gap*2;
  echo("screw_hole", screw_hole);
hinge_size=(n_hinge*2-1)*hinge_thk+(n_hinge-1)*hinge_gap*2;
  echo("hinge_size", hinge_size);
max_hinge_leng=max(hinge_leng, hinge_wid+1);
  echo("max_hinge_leng", max_hinge_leng);
max_hinge_offset=max(hinge_offset, head_dia+2, (base_dia+hinge_wid)/2);
  echo("max_hinge_offset", max_hinge_offset);
max_top_wid=max(top_wid, max_hinge_offset+base_dia/2+hinge_wid/2);
  echo("max_top_wid", max_top_wid);

//======== main ========//
rotate([0, 0, -90]){
if (part==0)
  translate([-max_hinge_offset, 0, base_hi+max_hinge_leng-hinge_wid/2])
    rotate([180, 0, 0])
    top_plate();
if (part==1||part==0)
  base_plate();
if (part==2)
  top_plate();
}//r
//

module top_plate(wid=max_top_wid,
                 leng=top_leng,
                 hi=top_hi,
                 hole=screw_hole,
                 hwid=hinge_wid,
                 hoffset=max_hinge_offset){
  ep=0.001;
  poffset=hoffset+hwid/2-wid/2;
  translate([hoffset, 0, -hi-hinge_leng+hinge_wid/2])
  union(){
    difference(){
      translate([-poffset, 0, 0])
        minkowski(){
          cube([wid, leng, ep], true);
          translate([0, 0, ep/2])
            bevel_ring(hi=hi-ep, e_rad=part_bevel, $fn=32);
        }//m
      if (slot_hole==0)
        translate([0, 0, -ep])
          linear_extrude(hi+ep*2)
          circle(d=hole);
      if (slot_hole==1){
        ds=leng-24; echo("ds", ds);
        hull(){
          for(i=[ds/2, -ds/2])
            translate([0, i, -ep])
              linear_extrude(hi+ep*2)
              circle(d=hole);
        }//h
      }//i
    }//d
    translate([-hoffset, 0, part_bevel])
      hinge(base=top_hi-part_bevel, n=n_hinge-1);
  }//u
}//m
//

module base_plate(dia=base_dia,
                  hi=base_hi,
                  hole=screw_hole,
                  hwid=hinge_wid,
                  hsize=hinge_size,
                  hoffset=max_hinge_offset){
  ep=0.001;
  difference(){
    hull(){
      bevel_ring(hi=hi, e_rad=dia/2);
      translate([-hoffset, 0, 0])
        minkowski(){
          cube([hwid, hsize, ep], true);
          translate([0, 0, ep/2])
            bevel_ring(hi=hi-ep, e_rad=part_bevel, $fn=32);
        }//m
    }//h
    translate([0, 0, -ep])
      linear_extrude(hi+ep*2)
      circle(d=hole);
  }//d
    translate([-hoffset, 0, part_bevel])
      hinge(base=base_hi-part_bevel);
}//m
//

//hinge();
module hinge( leng=max_hinge_leng,
              wid=hinge_wid,
              thk=hinge_thk,
              dia=screw_hole,
              base=base_hi,
              gap=hinge_gap,
              n=n_hinge){
  hinge_size=(n*2-1)*thk+(n-1)*gap*2;
  for (i=[-n/2+0.5:1:n/2-0.5])
    translate([0, i*(thk*2+gap*2), 0])
      translate([0, thk/2, base])
        rotate([0, -90, 90])
        eye_hook(leng, wid, thk, dia+gap*2);
  if (base>0)
    translate([0, 0, base/2])
      cube([wid, hinge_size, base], true);
}//m
//

module eye_hook(leng=20, wid=12, thk=4, dia=4){
  linear_extrude(thk)
  difference(){
    hull(){
      translate([0, -wid/2])
        square([leng-wid/2, wid]);
      translate([leng-wid/2, 0])
        circle(d=wid);
    }//h
    translate([leng-wid/2, 0])
      circle(d=dia);
  }//d
}//m
//

module knob(hi=6, rad=6, n=8){
  fin=max(min(hi/8, rad/6), 0.75);
  echo("fin", fin);
  difference(){
    linear_extrude(hi)
    union(){
      circle(r=rad);
      if (n>0)
      for (i=[0:n-1])
        rotate([0, 0, i*360/n])
        translate([rad+fin, 0])
          rotate([0, 0, 135])
          square(fin*2);
    }//u
    for(i=[0, hi])
      translate([0, 0, i])
        rotate_extrude()
        translate([rad+fin, 0])
          rotate([0, 0, 135])
          square(fin*2.5, true);
  }//d
}//m
//

module bevel_ring(hi=10, e_rad=14, i_rad=0, t_bevel=part_bevel, b_bevel=part_bevel){
  rotate_extrude()
  difference(){
    translate([i_rad, 0])
      square([e_rad-i_rad, hi]);
    if (t_bevel>0)
      translate([e_rad, hi])
        rotate([0, 0, 45])
        square([t_bevel*sqrt(2), t_bevel*sqrt(2)], true);
    if (b_bevel>0)
      translate([e_rad, 0])
        rotate([0, 0, 45])
        square([b_bevel*sqrt(2), b_bevel*sqrt(2)], true);
  }//d
}//m
//

```