[User]
Hi! I'm looking to create a parametric design for a MOLLE tag that can be customized with an arbitrary SVG design. I'd like the design to be able to be either raised or inset into the tag, and be compatible with both single-color and multi-color printing. I've been using OpenSCAD for a while, so I'm familiar with the basic concepts. Here's what I have in mind:

**Core Features:**

*   **Base Tag:** The base of the tag should be rectangular with rounded edges and a slot that allows for attachment to a MOLLE system, including the retaining teeth.
*   **SVG Design:** An SVG file should be imported and scaled to fit within the tag, optionally rotated.
*   **Extrusion/Inset:** The SVG design should be able to be either extruded from the surface of the base tag or inset into it.
*   **Multimaterial Support:** For inset designs, I'd like to be able to add a separate, thinner plate in the shape of the SVG for multi-material printing. I also need an option for this color plate to just be a square that's masked by the design shape.
*   **Parametric Design:**  All of the dimensions should be configurable parameters, including the base tag dimensions, slot dimensions, design depth, scaling, offsets, and edge styles.

**Specific Parameters (with example values):**

*   **SVG File:** `filename = "my_design.svg"`
*   **Design Depth:** `design_depth = 2.0` (depth to extrude or inset the design)
*   **Design Border:** `design_border = 3.0` (space between the design and the tag edge)
*   **Rotation:** `design_rotation = 90.0` (degrees of rotation for design)
*   **Design Offset:** `design_x = 0.0`, `design_y = 0.0`
*   **Scaling Axis:** `scale_axis = "X"` or `scale_axis = "Y"` (determines which dimension to use for scaling)
*   **Inset Design:** `inset = false` (toggles between raised and inset designs)
*   **Color Depth:** `color_depth = 1.0` (thickness of the color layer for inset designs)
*   **Shaped Color Plate:** `shaped_color_plate = true` (whether color plate is SVG shaped or square)
*   **Base Dimensions:** `base_len = 52.0`, `base_wid = 34.0`, `base_height = 4.0`
*   **Edge Feature:** `edge_type = "fillet"`, `edge_feature_size = 1.0` (fillet or chamfer)
*   **Strap Slot:** `slot_thick = 5.0`, `slot_wid = 26.0`
*   **Retainer Teeth:** `tooth_thick = 4.0`, `mouth_wid = 10.0`
*   **Multimaterial Mode:** `multimaterial = false` (output a separate part for the design)
*  **Output Toggles:** `render_base = true`, `render_design = true` (controls whether base and design are rendered, mostly for multimaterial printing)
* **Preview Colors:** `base_color = [0.6, 0.6, 0.6]`, `design_color = [1.0, 0.5, 0.0]` (for visualization, not part of the exported mesh)

**Other Considerations:**

*   I don't need any external libraries for this.
*   The base tag design should use rounded edges through `filletCube` or `chamferCube` modules, depending on user's `edge_type` parameter.
*   The automatic scaling should ensure that the imported SVG is fully contained within the tag without overflowing the boundaries specified by `design_border`, making use of `resize` with the `auto` parameter.
*   The SVG import should be centered both horizontally and vertically on the tag.
* The module `design_plate` should perform the rotation, scaling and extrusion of the SVG file.

I think that about covers it. Could you please create a SCAD file based on this? Let me know if you have any questions. Thanks!

[Assistant]
Tag_Customizable.scad:
```
/* [Design Parameters] */
//Name/Path of SVG file
filename = "Orange_Lambda.svg";
//Depth to extrude design above/below surface
design_depth = 2.0; //0.1
//Thickness of border between design and actual edge
design_border = 3.0; //0.1
//How much to rotate the design (degrees)
design_rotation = 90.0; //[0:1:360]
//Horizontal offset of design
design_x = 0.0; //0.1
//Vertical offset of design
design_y = 0.0; //0.1
//Change which axis is used for auto-scaling
scale_axis = "X"; //[X, Y]
//Sink design into base rather than place it on top
inset = false;
//Thickness of colored layers for inset design (only for multimaterial)
color_depth = 1.0; //0.1
//Make color plate the same shape as the design (otherwise just a square)
shaped_color_plate = true;

/* [Base Parameters] */
//Length of dimension parallel to strap
base_len = 52.0; //0.1
//Length of dimension perpendicular to strap
base_wid = 34.0; //0.1
//Thickness of base
base_height = 4.0; //0.1
//Type of edge feature
edge_type = "fillet"; //[fillet, chamfer]
//Depth of chamfers or radius of fillets, depending on selection
edge_feature_size = 1.0; //0.1

//Thickness of the interior of the strap slot
slot_thick = 5.0; //0.1
//Width of the strap slot
slot_wid = 26.0; //0.1
//Thicknes of retainer teeth
tooth_thick = 4.0; //0.1
//Width of gap between retainer teeth
mouth_wid = 10.0; //0.1

/* [Output Options] */
//Include base in output
render_base = true;
//Include design in output
render_design = true;
//Remove overlap so design is a separate piece for multimaterial printing
multimaterial = false;
//Preview color of base (RGB, not retained in mesh)
base_color = [0.6, 0.6, 0.6]; //[0:0.01:1]
//Preview color of design (RGB, not retained in mesh)
design_color = [1.0, 0.5, 0.0]; //[0:0.01:1]

/* [Hidden] */
tooth_len = (base_wid - mouth_wid)/2;
arm_thick = (base_wid - slot_wid)/2;
slot_back = slot_thick + tooth_thick;
partial_len = base_len - slot_back;
center_len = base_len - 2*slot_back;
canvas_size = min(base_wid, center_len) - design_border;
design_scale = (scale_axis == "X") ? [0,canvas_size] : ((scale_axis == "Y") ? [canvas_size,0] : [0,0]);
true_depth = multimaterial ? design_depth + color_depth : design_depth;

circle_res = 90;

module filletCube(size, r=1){
    translate([r, r, r])
        minkowski(){
            cube(size=[size[0] - 2*r, size[1] - 2*r, size[2] - 2*r]);
            sphere(r=r, $fn=circle_res);
        }
}

module chamferCube(size, d=1){
    x = d/sqrt(2);
    octo_points = [
        [x,0,0],
        [-x,0,0],
        [0,x,0],
        [0,-x,0],
        [0,0,x],
        [0,0,-x]];
    octo_faces = [
        [0, 2, 5],
        [2, 1, 5],
        [1, 3, 5],
        [3, 0, 5],
        [0, 4, 2],
        [2, 4, 1],
        [1, 4, 3],
        [3, 4, 0]];
    translate([x, x, x])
        minkowski(){
            cube(size=[size[0] - 2*x,size[1] - 2*x,size[2] - 2*x]);
            polyhedron(points=octo_points, faces=octo_faces);
        }
}

module edgeCube(size, d){
    if(edge_type == "chamfer"){
        chamferCube(size=size, d=d);
    }else if(edge_type == "fillet"){
        filletCube(size=size, r=d);
    }else{
        cube(size=size);
    }
}

module molle_base(){
    union(){
        translate([0, slot_back, 0])
            edgeCube(size=[base_wid, center_len, base_height], d=edge_feature_size);

        for(dx=[0,base_wid-arm_thick]){
            translate([dx, 0, 0])
                edgeCube(size=[arm_thick, base_len, base_height], d=edge_feature_size);
        }

        for(dx=[0,base_wid-tooth_len]){
            for(dy=[0,base_len-tooth_thick]){
                translate([dx, dy, 0])
                    edgeCube(size=[tooth_len, tooth_thick, base_height], d=edge_feature_size);
            }
        }
    }
}

module design_plate(height, thick){
    translate([base_wid/2 + design_y, base_len/2 + design_x, height])
        resize(design_scale, auto=[true,true])
            rotate([0, 0, design_rotation])
                linear_extrude(height=thick)
                    import(file=filename, center=true);
}

module color_plate(){
    translate([(base_wid - canvas_size)/2, (base_len - canvas_size)/2, base_height - true_depth])
        cube(size=[canvas_size, canvas_size, color_depth]);
}

if(render_base){
    color(base_color)
        if(inset){
            difference(){
                molle_base();

                design_plate(height = base_height - true_depth, thick = true_depth + 0.001);

                if(!shaped_color_plate){
                    color_plate();
                }
            }
        }else{
            molle_base();
        }
}
if(render_design){
    color(design_color)
        if(inset){
            if(multimaterial){
                if(shaped_color_plate){
                    design_plate(height = base_height - design_depth - color_depth, thick = color_depth);
                }else{
                    color_plate();
                }
            }
        }else if(multimaterial){
            design_plate(height = base_height, thick = design_depth);
        }else{
            design_plate(height = base_height/2, thick = base_height/2 + design_depth);
        }
}
```


[User]
Hey, I'm looking for a custom text ring design, similar to the "Pumpkin Text Ring" I saw on Thingiverse. I want it to be parametric so I can easily change the text, sizing, and how the text is displayed.

Essentially, it's a ring where I can specify text that wraps around its circumference. I need to be able to control the following:

1.  **Text Input:**  I should be able to enter any text I want, ideally, it would be displayed all in uppercase, spaced evenly around the ring.  It would be useful if repeating text was also an option to help get the right spacing.

2.  **Ring Diameter:**  I need control over both the inner and outer diameters of the ring itself.  I'd like to adjust these with a tolerance value, that is, an offset in mm.

3.  **Text Display Style:** I need options for how the text is displayed.
    *   **Raised:** The text would be raised above the surface of the ring.
    *   **Indented:** The text would be recessed or inset into the ring surface.
    *   **Punch Through:**  The text would cut all the way through the thickness of the ring.

4.  **Text Height/Depth:**  I need to be able to set how tall the raised text is, how deep the indented text is, and whether or not the text punches all the way through.

5.  **Font:** It would be great to be able to choose different fonts, but I would also like a fallback to a sans-serif font. It's important that the "punch through" option would use a stencil font to hold the letters in place.

6.  **Print Quality:** To make sure the printed parts look good, can you make sure to use the `$fa` and `$fs` variables for the number of facets and the minimum number of fragments of circles?

Can you generate the OpenSCAD code for this? I don't need any external libraries. Just the core language capabilities should be enough, I think. I'm imagining this will generate one .scad file.

[Assistant]
Pumpkin_Text_Ring.scad:
```
/*
   Pumpkin Text Ring customizer
   G. A. Tielke, October 2021

   User is to specify the text. The letters will be spaced
   apart so as to cover all 360 degrees of the circle.
   - All upper case will print more clearly
   - To get the spacing appropriate, add spacing or repeat text.
   - Between about 30 to 50 characters prints well

   Prints text as Raised or Indented
   - Indented either partially or through entire ring thickness

   Note: This may take a few minutes to render due to its
   complexity.
   HAPPY HOLLOWEEN - HAPPY HOLLOWEEN -
*/

/* [Text to print on the Ring] */
// Uppercase prints better, add spacing if needed
Ring_Text = "HAPPY HOLLOWEEN - HAPPY HOLLOWEEN - ";

/* [Print Style, Set ONLY ONE to 1] */
// Raise text above a thin ring (1=Yes)
Print_Raised = 0; // [0:1]
// Indent text into a thicker ring (1=Yes)
Print_Indented = 0; // [0:1]
// Text to go through entire thickness (1=Yes)
Punch_Through = 1; // [0:1]

/* [Adjust ring size for looser fit] */
// Increase inner circle, % of 1mm
Inner_Tolerance = 0; //[0:5:100]
// Decrease outer circle, % of 1mm
Outer_Tolerance = 0; //[0:5:100]

/* [Hidden] */

// Standard settings to improve quality of the print:
$fa = 1;
$fs = 0.4;

// Set numerical parameters based on user input:
RingHt = (Print_Raised) ? 0.5 : 2.0;
TextBase = (Print_Indented) ? 0.4 : -0.1;
TextHt = (Punch_Through) ? 3.001 : 2.001;

/*
  Create the ring, as a difference between two cylinders.
  If the text is indented or punched through, also
  subtract that text.
*/
difference() {
    cylinder(r=102.5/2-Outer_Tolerance/200,h=RingHt);
    scale([1,1,1]) {
        translate([0,0,-.05])
            cylinder(r=86.5/2+Inner_Tolerance/200,h=RingHt + 0.1);

    if (!Print_Raised) PrintText(TextBase,TextHt);
    }
}
/*
   If the text is raised, add that to the resuling ring.
*/
if (Print_Raised) PrintText(TextBase,TextHt);

/*
   Module for printing the actual text.
   The text begins at z=TextBase, and has a height of TextHt.
*/
module PrintText(TextBase,TextHt) {
    for (i=[0:len(Ring_Text)]) {

        rotate(-i*365/(len(Ring_Text)+1),[0,0,1])
        translate([0,89/2-0.5,TextBase])
        linear_extrude(height = TextHt)
        {
            /*
              A font style of "Stencil" is used for punching
              through. Otherwise the inner parts of some letters
              will not stay in place.
            */
            if (Punch_Through)
                text(Ring_Text[i],
                    font = "Stencil:style=Regular",
                    size=6.5, spacing=1.0,
                    halign="center", valign="baseline");
            else
                // If not punched through, use a standard font.
                text(Ring_Text[i],
                    font = "Liberation Sans:style=Bold",
                    size=6.5, spacing=1.0,
                    halign="center", valign="baseline");
        }
    }
}
```


[User]
Hey, I'm looking to create a parametric model for a bubble wheel, kind of like a centrifugal blower but with some specific tweaks. I've got a basic idea of what I want, and I'd love it if you could generate the OpenSCAD code for it.

Here's what I'm thinking:

*   **Overall Shape:**  It should have a cone-like base that's essentially a frustum, and the top of the cone should be flat to act as a bearing surface (so I need to be able to control the cone height).  Then there's a cylindrical ring on both the top and bottom of the cone, sort of creating a channel for the blades.
*   **Blades:**  There should be blades that are attached to the ring, extending outward from the center. They're not radial, they should be at an angle to the rings like a centrifugal impeller. I need to control the angle of these blades. These should not go all the way to the bottom of the ring, like a centrifugal impeller. I also need to specify how many blades are on the wheel.
*   **Blade Holes:**  I want to punch some holes through each blade, arranged in a line down the length of the blade. I need to control the number of holes on each blade and the radius of the holes.
*   **Parameters:** I'm looking to control the following values (all in millimeters unless otherwise specified):
    *   `FAN_WIDTH`: The overall width/diameter of the wheel.
    *   `FAN_HEIGHT`: The height of the entire wheel assembly (from bottom ring to top of the top ring).
    *   `RING_HEIGHT`: The height of the top and bottom rings.
    *   `RING_WIDTH`: The width/thickness of the top and bottom rings.
    *   `BLADE_COUNT`: The number of blades around the wheel.
    *   `BLADE_WIDTH`:  The width/thickness of each blade.
    *   `BLADE_ANGLE`: The angle of each blade, in degrees.
    *   `SHAFT_DIAMETER`: The diameter of the central hole through the cone base
    *   `CONE_HEIGHT`: The height of the cone part, allowing the user to make the bottom bearing surface flat
    *    `BLADE_HOLE_COUNT`: The number of holes on each blade.
    *   `BLADE_HOLE_RADIUS`: The radius of the holes on each blade.

I think that should be it. I'm excited to see what you come up with!

[Assistant]
bubble_wheel.scad:
```
// USER-DEFINED VARIABLES (IN MILLIMETERS)
FAN_WIDTH = 225;
FAN_HEIGHT = 90;
RING_HEIGHT = 5;
RING_WIDTH = 5;
BLADE_COUNT = 20;
BLADE_WIDTH = 40;
BLADE_ANGLE = 10;	// Degrees
SHAFT_DIAMETER = 12;
CONE_HEIGHT = 10;
BLADE_HOLE_COUNT = 3;
BLADE_HOLE_RADIUS = 11;

// CALCULATED VALUES (DO NOT CHANGE)
bottomRadius = FAN_WIDTH / 2;
topRadius = bottomRadius * 0.25;
ringCenterRadius = bottomRadius - RING_WIDTH;
shaftRadius = SHAFT_DIAMETER / 2;

// CONE
difference()
{
	cylinder(r1 = bottomRadius, r2 = topRadius, h = CONE_HEIGHT, $fn = 100);
	cylinder(r1 = shaftRadius, r2 = shaftRadius, h = CONE_HEIGHT, $fn = 100);
}

// TOP RING
translate([0, 0, FAN_HEIGHT - RING_HEIGHT])
difference()
{
	cylinder(r1 = bottomRadius, r2 = bottomRadius, h = RING_HEIGHT, $fn = 100);
	cylinder(r1 = ringCenterRadius, r2 = ringCenterRadius, h = RING_HEIGHT, $fn = 100);
}

// BOTTOM RING
difference()
{
	cylinder(r1 = bottomRadius, r2 = bottomRadius, h = RING_HEIGHT, $fn = 100);
	cylinder(r1 = ringCenterRadius, r2 = ringCenterRadius, h = RING_HEIGHT, $fn = 100);
}

// BLADES
rotate( 90, [0, 1, 0] )
for ( i = [0 : BLADE_COUNT - 1] )
{
    rotate(i * 360 / BLADE_COUNT, [1, 0, 0])
    translate([-FAN_HEIGHT, bottomRadius - RING_WIDTH, 0])
    rotate(BLADE_ANGLE, [1, 0, 0])
   {
       difference(){
        cube([FAN_HEIGHT, RING_WIDTH, BLADE_WIDTH], center = false);

        for ( i = [0 : BLADE_HOLE_COUNT - 1] )
        {
            reserved = RING_HEIGHT * 2;
            section = (FAN_HEIGHT - reserved) / BLADE_HOLE_COUNT;
            offset = section / 2;
            holes_width = BLADE_HOLE_COUNT * BLADE_HOLE_RADIUS;
            translate([((section * i) + offset + reserved / 2), -RING_HEIGHT, BLADE_WIDTH / 2])
            rotate(BLADE_ANGLE - 180, [0, 1, 1])
         cylinder(r=BLADE_HOLE_RADIUS,h=BLADE_WIDTH,$fn=100);
        }
    }
    }
}
```


[User]
Okay, I'm looking to create a custom filament roller for my 3D printer, similar to the one described in the text you've provided. It uses a print-in-place bearing design, which is pretty neat. I like how customizable the V4 version is, but want to make some adjustments and have the entire thing created from scratch for me.

Here are the basic requirements that I would like to see in my design:

**Overall Structure:**

*   The roller should consist of an inner shell, an outer shell, and a set of needle bearings that fit between them.
*   The inner shell should slide onto the printer's filament axle (36mm diameter).
*   The outer shell should be designed to accommodate filament spools, with a minimum inner diameter of around 52mm (but this should be a configurable parameter).
*   I need a flange on the outside edge of the roller to keep the spool from sliding off the side. The flange needs a configurable thickness and width.
*   The whole thing should be designed for print-in-place assembly, no extra parts needed!

**Detailed Dimensions and Parameters (All user-configurable):**

*   **`outer_od`**:  Outer diameter of the outer shell. This should be configurable and should allow for spools with different minimum inner diameters.
*   **`outer_t`**:  Thickness of the outer shell.
*   **`inner_id`**: Inner diameter of the inner shell (should be 36.5mm to fit on the axle).
*   **`inner_t`**: Thickness of the inner shell.
*   **`roller_h`**: Overall height of the roller.
*   **`flange_t`**: Thickness of the flange.
*   **`flange_w`**: Width of the flange (how far it extends beyond the outer shell).
*   **`needle_shell_clr`**: Clearance between the needle bearing and the shells.
*   **`needle_needle_clr`**: Clearance between the needle bearings themselves.
*   **`needle_contact_h`**: Height of the wide section of the needle that contacts the shells.
*   **`needle_neck_h`**: Height of the neck of the needle bearing.
*   **`needle_neck_d`**: Diameter of the needle neck.
*   **`needle_angle`**: Angle of the needle cavity from vertical.
*   **`shelf_penet_h`**: Depth that the needle point penetrates the printing shelf during printing.
*   **`shell_facets`**: Number of facets used for the shell when using rotate_extrude.
*   **`needle_facets`**: Number of facets used for the needle bearing when using rotate_extrude.

**Additional Features:**
*   **Cross-Section View:** I would love an option to generate a cross-section view for debugging, and for easy comparison against the reference.
*   **Test Bearing:** An option to render only the bottom bearing for test prints would be really useful.

**Libraries:**

I do not need any additional libraries, as I believe that this can be completed using only basic OpenSCAD libraries.

Could you generate an OpenSCAD file (or files) that implements this design, making all the parameters user configurable through the Customizer? I think keeping the logic similar to what's in the provided file would be a great starting point. I'd appreciate the design being easy to modify in the future, as well.

[Assistant]
Filament_Roller4.scad:
```
// Filament Roller with Print-in-Place Bearings
//
// This roller slides over the Ender-3 filament axle (diam 36 mm and
// usable length 90 mm). The roller accomodates filament spools with
// an inside diameter down to 52 mm. That diameter (outer_od) can be
// increased, such as if only 55 mm spools are used. A flange on the
// outside end of the roller ensures the spool can't walk off.
//
// By Curt McDowell <maker@fishlet.com> 2022-01-23
// Licensed under the Creative Commons - Attribution - ShareAlike.
// https://creativecommons.org/licenses/by-sa/4.0/
//
// The bearing design was inspired by BoomZilla1, "Better 3D Printable
// Bearing", https://www.thingiverse.com/thing:2746804.
// That was a remix of Qube3Bot, "Thrust Tolerant Needle Bearing",
// https://www.thingiverse.com/thing:2543414.

// Outer shell outer diameter, small enough to fit inside spool
outer_od = 51.5;
// Outer shell thickness (1 mm is good)
outer_t = 1.0;
// Inner shell inner diameter, big enough to slide over axle
inner_id = 36.5;
// Inner shell thickness (1 mm is good)
inner_t = 1.0;
// Overall roller height (when vertical; short enough to fit on axle)
roller_h = 89.0;
// Flange thickness (Ender-3 axle flange is 3 mm thick)
flange_t = 3;
// Flange width from outer shell (when vertical; Ender-3 axle flange is 4 mm)
flange_w = 3;
// Clearance from needle bearing to shell
needle_shell_clr = 0.24;
// Clearance from needle to needle
needle_needle_clr = 0.24;
// Height of wide section of needle that contacts shells
needle_contact_h = 5;
// Height of needle neck
needle_neck_h = 1;
// Width of needle neck
needle_neck_d = 2.4;
// Needle cavity slopes angle from vertical
needle_angle = 40;
// Penetration of needle point into printing shelf
shelf_penet_h = 0.75;
// Number of facets on large shells
shell_facets = 240;
// Number of facets on small needles
needle_facets = 72;
// Render cross-section view (for debug)
cross_section_view = false;
// Render bottom bearing only (for test print)
bottom_bearing_only = false;

module _end_params() {}

//     ++ +------+ ++     Cross Sectional Diagram
//     || |      | ||
//     || |      A ||     1  Outer shell
//     || |  4   | ||     2  Inner shell
//     |\ \      / /|     3  Retainer ring bump (no contact w/needle)
//     | \ \    / / |     4  Needle bearing
//     |1 \ |  | / 2|     5  Needle neck
//     |  | |  | |  |     6  Needle point
//     | 3| |5 B |3 |     7  Printing shelf
//     |  | |  | |  |
//     |  / |  | \  |     Angles from vertical:
//     | / /E   \ \ |       Retainer ring = 45
//     |/ /      \ \|       Needle cavity = 40
//     || |      | ||       Needle point = 45
//     || |      A ||       Shelf = 45
//     || |      | ||
//     || \      / ||     A  Height needle contact area
//     ||  \    /C ||     B  Height needle neck
//     ||   \6 /   ||     C  Height needle point
//     |+----\/-+D ||     D  Height shelf penetration, also equals
//     |       /   ||        contact area radius (must break free)
//     |  7   /    ||     E  Needle neck drop, based on needle angle
//     |     /     ||
//     |    /      ||

$eps = 0.001;
$inf = 1000;

inner_ir = inner_id / 2;
inner_or = inner_ir + inner_t;

outer_or = outer_od / 2;
outer_ir = outer_or - outer_t;

shell_spacing = outer_ir - inner_or;

needle_center = (outer_ir + inner_or) / 2;
needle_d = shell_spacing - 2 * needle_shell_clr;
needle_r = needle_d / 2;
needle_neck_r = needle_neck_d / 2;
needle_neck_drop = needle_r - needle_neck_r;
needle_spacing = needle_d + needle_needle_clr;
needle_count = floor(2 * PI * needle_center / needle_spacing);

// Points defining profile of needle
np0 = [0, 0];                           // Bot tip of needle
np1 = np0 + [needle_r, needle_r];       // Bot of bot contact area
np2 = np1 + [0, needle_contact_h];      // Top of bot contact area
ndw = needle_r - needle_neck_r;         // Neck drop w
ndh = ndw / tan(needle_angle);          // Neck drop h
np3 = np2 + [-ndw, ndh];                // Bot of neck
np4 = np3 + [0, needle_neck_h];         // Top of neck
neck_center_h = (np3[1] + np4[1]) / 2;
np5 = np4 + [ndw, ndh];                 // Bot of top contact area
np6 = np5 + [0, needle_contact_h];      // Top of needle
needle_h = np6[1];                      // Needle overall height
np7 = [0, needle_h];                    // Center of top

needle_profile = [
    np0, np1, np2, np3, np4, np5, np6, np7
];

module needle()
    rotate_extrude($fn = needle_facets, convexity = 4)
        polygon(needle_profile, convexity = 4);

module needles()
    for (i = [1 : needle_count])
        rotate (i * 360 / needle_count) {
            translate([needle_center, 0, roller_h - needle_h])
                needle();
            translate([needle_center, 0, needle_h])
                mirror([0, 0, 1])
                    needle();
        }

// Points defining profile of inner shell with two retainer rings
// The section of rr near the neck will have double the clearance
rr_w = needle_neck_drop - needle_shell_clr;
rr_h = rr_w;                             // For 45 degrees
rr_bump_center_h = needle_h - neck_center_h;
rr_bump_bot_h = rr_bump_center_h - needle_neck_h / 2;
rr_bot_h = rr_bump_bot_h - rr_w;         // For 45 degrees
ip0 = [inner_ir, 0];                     // Bot inner edge
ip1 = [inner_ir + inner_t, 0];           // Bot outer edge
ip2 = ip1 + [0, rr_bot_h];               // Bot rr bot
ip3 = ip2 + [rr_w, rr_h];                // Bot rr hump lower
rr_inner_max = ip3[0];                   // For sanity check below
ip4 = ip3 + [0, needle_neck_h];          // Bot rr hump upper
ip5 = ip4 + [-rr_w, rr_h];               // Bot rr top
rr_top_h = ip5[1];
ip6 = [inner_ir + inner_t, roller_h - rr_top_h];   // Top rr bot
ip7 = ip6 + [rr_w, rr_h];                // Top rr hump lower
ip8 = ip7 + [0, needle_neck_h];          // Top rr hump upper
ip9 = ip8 + [-rr_w, rr_h];               // Top rr top
ip10 = [inner_ir + inner_t, roller_h];   // Top outer edge
ip11 = [inner_ir, roller_h];             // Top inner edge

inner_profile = [
    ip0, ip1, ip2, ip3, ip4, ip5, ip6, ip7, ip8, ip9, ip10, ip11
];

module inner()
    rotate_extrude($fn = shell_facets, convexity = 4)
        polygon(inner_profile, convexity = 4);

// Points defining profile of outer shell with two retainer rings,
// a shelf for the needles below the top retainer ring, and a
// triangular (45-90-45) stiffening ring half way up the shell

shelf_w_extra = shelf_penet_h;           // Shelf extends slightly past
shelf_w = outer_ir - needle_center + shelf_penet_h + shelf_w_extra;
shelf_h = roller_h - needle_h + shelf_penet_h;
op0 = [outer_ir, 0];                     // Bot inner edge
op1 = op0 + [outer_t + flange_w, 0];     // Bot flange outer
op2 = op1 + [0, flange_t];               // Top flange outer
op3 = op2 + [-flange_w, 0];              // Top flange inner
op4 = [outer_ir + outer_t, roller_h];    // Top outer edge
op5 = [outer_ir, roller_h];              // Top inner edge
op6 = op5 + [0, -rr_bot_h];              // Top rr top
op7 = op6 + [-rr_w, -rr_w];              // Top rr hump upper
op8 = op7 + [0, -needle_neck_h];         // Top rr hump lower
op9 = op8 + [rr_w, -rr_w];               // Top rr bot
op10 = [outer_ir, shelf_h];              // Shelf top outer
op11 = op10 + [-shelf_w, 0];             // Shelf top inner
op12 = op11 + [shelf_w, -shelf_w];       // Shelf bottom outer
sr_w = rr_w;     // Stiffening ring width matches rr (no reason)
op13 = [outer_ir, roller_h / 2 + sr_w];  // Sr top
op14 = [outer_ir - sr_w, roller_h / 2];  // Sr point
op15 = [outer_ir, roller_h / 2 - sr_w];  // Sr bot
op16 = [outer_ir, rr_top_h];             // Bot rr top
op17 = op16 + [-rr_w, -rr_w];            // Bot rr hump upper
rr_outer_min = op14[0];                  // For sanity check below
op18 = op17 + [0, -needle_neck_h];       // Bot rr hump lower
op19 = op18 + [rr_w, -rr_w];             // Bot rr

outer_profile = [
    op0, op1, op2, op3, op4, op5, op6, op7, op8, op9,
    op10, op11, op12, op13, op14, op15, op16, op17, op18, op19
];

module outer()
    rotate_extrude($fn = shell_facets, convexity = 6)
        polygon(outer_profile, convexity = 6);

module product ()
    union(convexity = 10) {
        inner();
        outer();
        needles();
    }

echo(str("Distance between shells: ", shell_spacing));
echo(str("Needle diam: ", needle_d));
echo(str("Needle neck diam: ", needle_neck_d));
echo(str("Needle neck drop: ", needle_neck_drop));
echo(str("Needle overall height: ", needle_h));
echo(str("Needle neck center height: ", neck_center_h));
echo(str("Needle count: ", needle_count));
echo(str("Distance between retainer rings: ",
         rr_outer_min - rr_inner_max));

intersection(convexity = 6) {
    product();
    if (cross_section_view)
        translate([-$inf / 2, 0, -$eps])
            cube([$inf, $inf, roller_h + 2 * $eps]);
    if (bottom_bearing_only)
        translate([0, 0, -$eps])
            cylinder(r = outer_or, h = needle_h - needle_r,
                     $fn = shell_facets);
}

```


[User]
I'm looking to design some custom mounting plates for my IKEA SKÅDIS pegboard, and I want a lot of flexibility. I've seen some examples, but I really need something that's configurable.

Ideally, the design should be parametric and let me control things like:

*   **Base Plate Dimensions:** I need to set the width, height, and thickness of the main plate.
*   **SKÅDIS Hooks:** I want to be able to choose if it should have just a single row of hooks or multiple rows, and also if those should be a simple grid or a staggered pattern for more strength. I need the hook dimensions to be based on the SKÅDIS standard, but with some ability to tweak the tolerances to achieve a snug fit if needed.
*   **Mounting Holes:** It needs to be able to generate screw holes for attaching things, including the number of holes, the horizontal and vertical spacing between them, their diameter, and rendering quality. Ideally, it'd also be able to add recesses for countersunk hex nuts, and I'd need to be able to control those dimensions (diameter, depth, and tolerance).
*  **Mounting Studs:** I want to be able to add standoff studs for attaching things without using screws, like a small router or something. I need to specify how many studs, their spacing (horizontal and vertical), length, diameter, head diameter, head thickness, and if they should be conical or cylindrical. The rendering quality also has to be customizable here.
*   **Splitting the Plate:** For larger plates, it'd be great if I could split the plate into two pieces (top and bottom), so I can print them flat and easily without needing a ton of supports and then glue them together. If the bottom plate is laid flat, then I may want to displace the top plate away so that they don't overlap and get in the way of one another. For the split part, I'd need to control the split ratio, which parts are generated, and which are laid flat.

I'd also like the code to use the `minkowski()` and `rotate()` functions in a way that is easily re-rendered by the OpenSCAD system.

I'm hoping to have the code generate separate modules for the SKÅDIS hooks, the mounting holes, and studs so that they are easily modified as needed. I'd also like it to have a module to generate the baseplate, with the option of adding studs or holes. It would be convenient if there was a final module that puts all of the pieces together and generates the final mounting plate.

Could you generate an OpenSCAD file for me? Thanks!

[Assistant]
generic-skadis-mounting-plate.scad:
```

/* generic-skadis-mounting-plate.scad
 * Mounting plates for IKEA SKADIS pegboard
 *
 * Copyright (c) 2021 "Philipp Reichmuth" <philipp.reichmuth@gmail.com>
 *
 * License: CC-BY-SA-NC 4.0
 * https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode
 *
 * Partly uses code by Guido Krömer (under MIT license)
 */

// Parameters of mounting plate

/* [Dimensions] */
BASEPLATE_WIDTH  = 119;
BASEPLATE_HEIGHT = 19;
BASEPLATE_THICKNESS = 2;

// Generate multiple rows of hooks for attaching heavier objects.
MULTIPLE_HOOK_ROWS = false;
// Generate rows of diagonal center hooks for extra strength.
DIAGONAL_HOOKS     = true;

/* [Mounting holes] */
// Generate holes for attahing objects with screws
GENERATE_HOLES = false;
// Number of holes, horizontally ("columns")
HOLE_COUNT_H = 2;
// Horizontal distance between holes
HOLE_DISTANCE_H = 20;
// Number of holes, vertically ("rows")
HOLE_COUNT_V = 1;
// Vertical distance between holes
HOLE_DISTANCE_V = 30;
// Diameter of each hole in millimeter
HOLE_DIAMETER = 4;
// Rendering quality ("roundness")
HOLE_QUALITY = 20; // [10:20]

// Recesses for countersunk hex nuts
GENERATE_NUT_RECESSES = true;
// Countersunk nut diameter (6mm for M3)
HOLE_NUT_DIAMETER = 6;
// Countersunk nut depth (3mm for M3)
HOLE_NUT_DEPTH = 3;
HOLE_NUT_TOLERANCE = 0.5;

/* [Device mounting studs] */
// Generate standoff studs for attaching devices
GENERATE_STUDS = false;
// Number of studs, horizontally ("columns")
STUD_COUNT_H = 1;
// Horizontal distance between studs
STUD_DISTANCE_H = 50;
// Number of studs, vertically ("rows")
STUD_COUNT_V = 2;
// Vertical distance between studs
STUD_DISTANCE_V = 100;
STUD_LENGTH = 4;
STUD_DIAMETER = 3;
// The stud head is what goes into the slot at the device's back
STUD_HEAD_DIAMETER = 5;
STUD_HEAD_THICKNESS = 1;
// Conical (screw-like) or cylindrical stud heads
STUD_HEAD_CONICAL = true;
// Rendering quality ("roundness")
STUD_QUALITY = 20; // [10:20]

// Optionally split the plate and lay the parts flat
// In this way, the two halves can be printed with minimal supports
// and later glued together

/* [Minimizing Supports] */
// Split the back plate into two halves for easier printing
SPLIT_PLATE = false;
// Default: beefier bottom with hooks, thinner top with studs
SPLIT_RATIO = 0.6;
// Generate the bottom part that attaches to the pegboard
GENERATE_BOTTOM = true;
// Generate the top part to which things are attached
GENERATE_TOP = true;
// Lay the bottom part flat (easier printing, but hooks will need support)
FLATTEN_BOTTOM = false;
// Lay the top part flat (so that studs can be printed without support)
FLATTEN_TOP = false;

// No more customizing after here
module __Customizer_Limit__ () {}

// Standard parameters for IKEA SKADIS pegboard
SKADIS_TOLERANCE = 1; // Adjust this for tighter fit

// No customizing after here
SKADIS_DISTANCE  = 40;
SKADIS_DIAG_DISTANCE = 20;
SKADIS_HOOK_WIDTH = 5;
SKADIS_HOOK_LENGTH = 10;

skadis_hook_side_width = SKADIS_HOOK_WIDTH-SKADIS_TOLERANCE;
skadis_row_count = floor( BASEPLATE_HEIGHT
                          / (SKADIS_DISTANCE
                             + SKADIS_HOOK_WIDTH) );
skadis_column_count = floor( BASEPLATE_WIDTH
                             / (SKADIS_DISTANCE
                               + SKADIS_HOOK_WIDTH) );
skadis_offset_h = 0.5 * (BASEPLATE_WIDTH - skadis_column_count*SKADIS_DISTANCE - SKADIS_HOOK_WIDTH);

// Offsets for placing screwholes
hole_offset_h = (BASEPLATE_WIDTH
                 - ( (HOLE_COUNT_H-1) * HOLE_DISTANCE_H) ) /2;
hole_offset_v = (BASEPLATE_HEIGHT
                 - ( (HOLE_COUNT_V-1) * HOLE_DISTANCE_V) ) /2;

// Offsets for placing mounting studs
stud_offset_h = (BASEPLATE_WIDTH
                 - ( (STUD_COUNT_H-1) * STUD_DISTANCE_H) ) /2;
stud_offset_v = (BASEPLATE_HEIGHT
                 - ( (STUD_COUNT_V-1) * STUD_DISTANCE_V) ) /2;

// Helpers

// Symmetrical constructions
module copy_mirror(vec=[1,0,0])
{
    children();
    mirror(vec) children();
}

// Laying things flat by rotating and translating them in the YZ plane
module lay_flat_yz(offset, angle=-90)
{
    multmatrix(m = [ [1,          0,          0, 0 ],
                     [0, cos(angle),-sin(angle), 0 ],
                     [0, sin(angle), cos(angle), offset ],
                     [0,          0,          0, 1 ] ])
        children();
}

// Split and object in Y-direction at a given cutoff point,
// lay the top or bottom half flat for printing without supports
module split_flat_y(
    y_cutoff, // Cutoff point
    x_min, x_max, y_min, y_max, z_min, z_max, // Bounding box
    flatten_bottom = false,
    flatten_top = false,
    generate_bottom = true,
    generate_top = true,
    distance=1)
{

    // Determine the orientation for the various parts
    // Rotation angle for the bottom part ("forward")
    bottom_angle = flatten_bottom ? -90 : 0;
    // Offset for laying the bottom part flat, if necessary
    bottom_offset_y = flatten_bottom ? y_cutoff : 0;
    // Rotation angle for the top part ("backward")
    top_angle = flatten_top ? 90 : 0;
    // Offset for laying the top part flat, if necessary
    top_offset_y = flatten_top ? -y_cutoff : 0;
    // Optionally displace the top part horizontally,
    // if the bottom is laid flat and would get in the way
    top_displace_x = flatten_bottom
                         ? flatten_top
                             ? x_max + distance
                             : 0
                         : 0;
    // Move the bottom part out, depending on whether the bottom
    // is left standing or laid flat
    top_displace_y = flatten_bottom
                         ? z_max
                         : flatten_top
                             ? z_max + y_cutoff + distance
                             : 0;

    // Bottom part up to Y cutoff point
    if (generate_bottom)
    lay_flat_yz(bottom_offset_y, bottom_angle) // Angle is -90 or 0
    intersection() {
        children();
        translate([x_min, y_min, z_min])
            cube([x_max-x_min, y_cutoff-y_min, z_max-z_min]);
        echo("Bounding box: ", x_min, x_max, y_min, y_max, z_min, z_max);
        echo("Cutoff base coordinates: ", x_min, y_cutoff, z_min);
        echo("Cutoff cube size: ", x_max-x_min, y_max-y_cutoff, z_max-z_min);
    }

    // Top part from Y cutoff point outward
    // Optionally displace
    if (generate_top)
    translate([top_displace_x,top_displace_y,0])
    lay_flat_yz(top_offset_y, top_angle) // Angle is 0 or 90
    intersection() {
        children();
        translate([x_min, y_cutoff, z_min])
            cube([x_max-x_min, y_max-y_cutoff, z_max-z_min]);
        echo("Bounding box: ", x_min, x_max, y_min, y_max, z_min, z_max);
        echo("Cutoff base coordinates: ", x_min, y_cutoff, z_min);
        echo("Cutoff cube size: ", x_max-x_min, y_max-y_cutoff, z_max-z_min);
    }
};

// SKADIS hooks
// Taken from Guido Krömer's MIT-licensed SKADIS box generator
// Original file at https://www.thingiverse.com/download:7199040
module rounded_corner_cube(size, radius = 1,
                           resolution = 5, mode = "all_sides") {
  half_radius = radius / 2;

  if (mode == "all_sides") {
    translate([half_radius, half_radius, half_radius])
    minkowski() {
      resize(newsize = [radius, radius, radius])
      sphere(r = resolution);
      cube([size[0] - radius, size[1] - radius, size[2] - radius],
           false);
    }
  } else if (mode == "vertical_sides") {
    translate([half_radius, half_radius, 0])
    minkowski() {
      resize(newsize = [radius, radius, radius])
      cylinder(r = resolution, h = resolution);
      cube([size[0] - radius, size[1] - radius, size[2] - radius],
           false);
    }
  } else if (mode == "horizontal_sides") {
    translate([half_radius, radius, half_radius])
    minkowski() {
      resize(newsize = [radius, radius, radius])
      rotate([90,0,0])
      cylinder(r = resolution, h = resolution);
      cube([size[0] - radius, size[1] - radius, size[2] - radius],
           false);
    }
  } else {
    cube(size);
  }
}

module skadis_hook(side_width, length) {
  render() // Save some Minkowski time if we have multiple hooks
    union() {
      cube([side_width, side_width, side_width]);
      rounded_corner_cube([side_width, length, side_width],
                           mode = "vertical_sides");
      translate([0, length - side_width, side_width - 1])
      rounded_corner_cube([side_width, side_width, length]);
  }
}

// Row of multiple SKADIS hooks
module skadis_hook_row(width,
                       hook_side_width = skadis_hook_side_width,
                       hook_length = SKADIS_HOOK_LENGTH) {
  union() {
    for(x_step = [0 : 40 : width]) {
      translate([x_step, 0, 0])
      skadis_hook(side_width = hook_side_width,
                  length = hook_length);
    }
  }
}

// 2D pattern of SKADIS hooks for mounting plates
// Generates a rectangular array of hooks (fill_diags = false),
// or a checkerboard with extra middle hooks (fill_diags = true).
module skadis_hook_plate(width, height, fill_diags = true,
    hook_side_width = skadis_hook_side_width,
    hook_length = SKADIS_HOOK_LENGTH) {
  union() {
    for(z_step = [0 : 40 : height]) {
        for(x_step = [0 : 40 : width]) {
          translate([x_step, 0, z_step])
          skadis_hook(side_width = hook_side_width,
                      length = hook_length);
        }
    };
    if(fill_diags) {
        for(z_step = [20 : 40 : height]) {
            for(x_step = [20 : 40 : width - SKADIS_DIAG_DISTANCE]) {
              translate([x_step, 0, z_step])
              skadis_hook(side_width = hook_side_width,
                          length = hook_length);
              echo("X-step: ", x_step, ", width: ", width, "; Z-step: ", z_step, ", height: ", height);
            }
        };
    };
  }
}

// 2D primitives

// 3D primitives

// Generate a mounting hole, with an optional recess for a hex nut.
module hole(dia = HOLE_DIAMETER)
{
    render() // Save a bit of time
    rotate([270,0,0])
    union() {
        cylinder(h = BASEPLATE_THICKNESS,
                 d = dia,
                 center = false,
                 $fn = HOLE_QUALITY);
        if (GENERATE_NUT_RECESSES)
            cylinder(h = HOLE_NUT_DEPTH + HOLE_NUT_TOLERANCE,
                     d = HOLE_NUT_DIAMETER + HOLE_NUT_TOLERANCE,
                     center = false,
                     $fn = 6);
    };
};

// Generate a mounting stud.
module stud(dia = STUD_DIAMETER, length = STUD_LENGTH,
            head_dia = STUD_HEAD_DIAMETER,
            head_thickness = STUD_HEAD_THICKNESS,
            conical = STUD_HEAD_CONICAL)
{
    render() // Save a bit of time
    rotate([270,0,0])
    union() {
        cylinder(h = length,
                 d = dia,
                 center = false,
                 $fn = STUD_QUALITY);
        translate([0,0,length-head_thickness])

            cylinder(h = head_thickness,
                     d1 = conical ? dia : head_dia,
                     d2 = head_dia,
                     center = false,
                     $fn = STUD_QUALITY);
    };
};

// Baseplate with any studs or holes
module baseplate(thickness = BASEPLATE_THICKNESS)
{
    union() {
        difference() {
            cube([BASEPLATE_WIDTH,
                  thickness, BASEPLATE_HEIGHT]);
            if (GENERATE_HOLES) {
                for(h_count = [0 : HOLE_COUNT_H-1]) {
                    for(v_count = [0 : HOLE_COUNT_V-1]) {
                        translate([hole_offset_h+h_count*HOLE_DISTANCE_H,
                                   0,
                                   hole_offset_v+v_count*HOLE_DISTANCE_V])
                        hole();
                        echo("H Offset: ", hole_offset_h,
                             "V Offset: ", hole_offset_v,
                             "H hole nr. ", h_count,
                             "V hole nr. ", v_count,
                             "hole X: ",
                                hole_offset_h+h_count*HOLE_DISTANCE_H,
                             "hole Y: ",
                                hole_offset_v+v_count*HOLE_DISTANCE_V);
                    }; // for [V]
                }; // for [H]
            }; // if [generate holes]
        }; // difference
        if (GENERATE_STUDS) {
            for(h_count = [0 : STUD_COUNT_H-1]) {
                for(v_count = [0 : STUD_COUNT_V-1]) {
                    translate([stud_offset_h+h_count*STUD_DISTANCE_H,
                               thickness,
                               stud_offset_v+v_count*STUD_DISTANCE_V])
                    stud();
                    echo("H Offset: ", stud_offset_h,
                         "V Offset: ", stud_offset_v,
                         "H stud nr. ", h_count,
                         "V stud nr. ", v_count,
                         "stud X: ",
                            stud_offset_h+h_count*STUD_DISTANCE_H,
                         "stud Y: ",
                            stud_offset_v+v_count*STUD_DISTANCE_V);
                }; // for [V]
            }; // for [H]
        }; // if [generate studs]
    }; // union
};

// SKADIS device mounting plate (baseplate with SKADIS hooks)
module generic_skadis_mounting_plate()
{
    union() {

        // Baseplate
        baseplate();

        // SKADIS hooks
        translate([skadis_offset_h,0,0])
            mirror([0,1,0])
                if(MULTIPLE_HOOK_ROWS)
                    // Generate pattern of hooks in multiple rows
                    skadis_hook_plate(BASEPLATE_WIDTH
                                      - skadis_offset_h
                                      - SKADIS_HOOK_WIDTH,
                                      BASEPLATE_HEIGHT
                                      - SKADIS_HOOK_WIDTH,
                                      DIAGONAL_HOOKS);
                else
                    // Generate a single row of hooks
                    skadis_hook_row(BASEPLATE_WIDTH
                                    - skadis_offset_h
                                    - SKADIS_HOOK_WIDTH);
    };
};

// Check if we split the plate.
// If we do, check which parts are generated and if they are laid flat.
// If we don't, generate everything and leave it standing.
fb = SPLIT_PLATE ? FLATTEN_BOTTOM : false;
ft = SPLIT_PLATE ? FLATTEN_TOP : false;
gb = SPLIT_PLATE ? GENERATE_BOTTOM : true;
gt = SPLIT_PLATE ? GENERATE_TOP : true;

split_flat_y(BASEPLATE_THICKNESS * SPLIT_RATIO,
             0, BASEPLATE_WIDTH,
             -SKADIS_HOOK_LENGTH, STUD_LENGTH+BASEPLATE_THICKNESS,
             0, MULTIPLE_HOOK_ROWS
                 ? BASEPLATE_HEIGHT + SKADIS_HOOK_LENGTH
                 : BASEPLATE_HEIGHT,
             flatten_bottom = fb,
             flatten_top = ft,
             generate_bottom = gb,
             generate_top = gt
)
    generic_skadis_mounting_plate();
```


[User]
Hi, I'm looking for help designing a parametric quarter-turn latch for a project box. I've used a design I found online (QuarterTurnBoxLatch.scad), but I'd like a more flexible version of it that I can tweak easily.

The basic concept is a rotating latch arm that fits through a hole in the box, a retainer with a pivot boss for the arm, and a clip that the arm engages with. The latch should have three detent positions – open, closed left, and closed right. The current version also has a sprue connecting the parts so it's convenient to print them together.

Here are some of the features I'd like:

1.  **Parametric Dimensions:** I want to be able to adjust the box wall thickness, the drill size for the arm, and the width of the screwdriver slot. The dimensions in the example are:
    *   `BOX_HOLE_DRILL_DIA = 10;` (This is the diameter of the hole in the box for the rotating arm)
    *   `WALL_THICK = 1.5;` (This is the thickness of the wall the latch will be mounted on)
    *   `COIN_WIDE = BOX_HOLE_DRILL_DIA/6;` (Width of the screwdriver slot on the rotating arm).

2.  **Detent Mechanism:** The latch should have a detent mechanism that "clicks" into place in three positions: 0 degrees (open), -90 degrees (closed left), and 90 degrees (closed right). This is achieved in the current design with small cylinders.

3.  **Separate Parts:** The latch consists of three parts:
    *   The **rotating arm** with a screwdriver slot and a disc that rotates in the hole
    *   The **retainer** which has a pivot boss that the arm snaps into, and the detent spring
    *   The **clip** which is similar to the retainer but without the pivot boss or spring. It receives the arm and provides the door gap.

4.  **Sprue:** The parts should be connected with a sprue for easy printing and storage. The connections should be easy to cut with an X-Acto knife, like in the example I'm including. I'd like a sprue between the clip and retainer and another from the arm to one of the clip/retainer.

5.  **Customizer-Friendly:** I'd like the design to be easy to modify through a customizer, so all the important parameters are exposed.

6.  **No Libraries (for now):** For this initial version, I'd like to avoid any external libraries; I'd like all of the code contained in the generated file(s).

I'm primarily interested in a single file solution right now, as I'm not yet very familiar with OpenSCAD and want to keep things simple.

Could you help me design this? Thanks!

[Assistant]
QuarterTurnBoxLatch.scad:
```
// Quarter turn electronic equipment box battery door latch
// Use a wide flat blade screwdriver to open/close
// All dimensions in mm
// Default latch is for a drill size of 10mm diameter
// Print at 100% (solid) infill from PETG or ABS
// written by Steve Malikoff ('1944GPW') in Brisbane, Australia
// Last update 2021-10-23
//
// FITTING INSTRUCTIONS
// 1) Remove arm from retainer and clip by cutting sprue
//    NOTE Do _not_ cut the two sprues linking the retainer to the clip yet!
// 2) Position the retainer/clip inside the box door so the door gap coincides with
//    the gap between these parts
// 3) Use a scriber or sharp pencil point through the circular boss to mark the centre
//    of the hole to be drilled
// 4) Remove retainer/clip and centrepunch the mark, pilot drill and drill to 10mm or
//    your arm disc size
// 5) Snap the arm onto the boss so it sticks out, invert and fit the arm disc through
//    your drilled hole
//    Ensure the hole is sufficent to not bind when turning the arm. Use a sharp X-acto blade
//    to deburr and enlarge the hole if necessary
// 6) Position the retainer/clip so the gap between them is straight across the door gap
// 7) Apply a drop of CA/Superglue to the short sides of the retainer and clip respectively
// 8) When set, cut the two sprues between the retainer and clip with an X-acto blade
// 9) Use a flat screwdriver blade to turn the latch arm and check it clicks into the
//    detent spring nub at the three positions
//

BOX_HOLE_DRILL_DIA = 10; //12.7		// Drill size for rotating arm
WALL_THICK = 1.5; 		 //1.6		// Plastic box wall thickness, or close to

// ----------------------------------------------------------------
module CustomiserStopper() {} // don't show any variables past here

SLOTTED_DISK_THICK = WALL_THICK * 1.2;
COIN_WIDE = BOX_HOLE_DRILL_DIA/6;

FACETS = 155;
$fn = FACETS;

/// Generate the model proper
QuarterTurnLatchComponentsOnSprue();

/// Arrange the three parts on a sprue so they can be printed off
/// and stored in a parts bin
module QuarterTurnLatchComponentsOnSprue()
{
	translate([0,0,WALL_THICK])
		color("lightgreen")
			Retainer();
	translate([BOX_HOLE_DRILL_DIA + WALL_THICK * 2 + 1, 0, WALL_THICK])
		rotate(180)
			color("grey")
				Clip();
	translate([(BOX_HOLE_DRILL_DIA + WALL_THICK*2) * 2 + 1, BOX_HOLE_DRILL_DIA*0.5, 0])
		rotate(270)
			QuarterTurnLatch();
	Sprues();
}

/// The moving arm with a screwdriver slot
module QuarterTurnLatch()
{
	Arm();
	translate([0,0,WALL_THICK])
		SlottedTopDisk();
}

module SlottedTopDisk()
{
	difference()
	{
		cylinder(d=BOX_HOLE_DRILL_DIA,h=SLOTTED_DISK_THICK);
		Slot();
	}
}

module Slot()
{
	translate([-BOX_HOLE_DRILL_DIA/2, -COIN_WIDE/2, WALL_THICK*0.6])
		cube([BOX_HOLE_DRILL_DIA, COIN_WIDE, SLOTTED_DISK_THICK - WALL_THICK*0.6]);
}

module Arm()
{
	difference()
	{
		translate([BOX_HOLE_DRILL_DIA/2, 0, 0])
			Obround(BOX_HOLE_DRILL_DIA* 2, BOX_HOLE_DRILL_DIA, WALL_THICK);
		cylinder(d=BOX_HOLE_DRILL_DIA - WALL_THICK*3, h=WALL_THICK);
		// Detent indents: back, both sides
		translate([-BOX_HOLE_DRILL_DIA/2, 0, 0])
			cylinder(d=WALL_THICK*1.5, h=WALL_THICK);
		translate([0, -BOX_HOLE_DRILL_DIA/2, 0])
			cylinder(d=WALL_THICK*1.5, h=WALL_THICK);
		translate([0, BOX_HOLE_DRILL_DIA/2, 0])
			cylinder(d=WALL_THICK*1.5, h=WALL_THICK);
	}
}

/// The fixture that holds the latch arm. It has a pivot boss and a built-in detent spring
/// to detent the arm to stay open at 0 degrees, or closed at -90 and 90 degrees (ie. each side)
module Retainer()
{
	difference()
	{
		RetainerSolid();
		translate([-BOX_HOLE_DRILL_DIA/2,-BOX_HOLE_DRILL_DIA*1.5,-WALL_THICK])
			cube([WALL_THICK,BOX_HOLE_DRILL_DIA*3, WALL_THICK*3]);
		translate([0,0,-WALL_THICK])
			cylinder(d=WALL_THICK, h=WALL_THICK*3);
	}
	translate([-BOX_HOLE_DRILL_DIA/2, 0, -WALL_THICK])
		cylinder(d=WALL_THICK*1.5, h=WALL_THICK*1.8);
}

module RetainerSolid()
{
	RetainerHalf();
	mirror([0,1,0])
		RetainerHalf();
	cylinder(d=BOX_HOLE_DRILL_DIA - WALL_THICK*3.05, h=WALL_THICK*0.9);
}

module RetainerHalf()
{
	difference()
	{
		RetainerHalfSolid();
		translate([0, BOX_HOLE_DRILL_DIA*1.75, -WALL_THICK])
			cylinder(d=WALL_THICK, h=WALL_THICK*3);
	}
}

module RetainerHalfSolid()
{
	translate([-BOX_HOLE_DRILL_DIA/2 - WALL_THICK, 0, -WALL_THICK])
		cube([BOX_HOLE_DRILL_DIA + WALL_THICK*2, BOX_HOLE_DRILL_DIA *2, WALL_THICK]);
	translate([-BOX_HOLE_DRILL_DIA/2 - WALL_THICK, BOX_HOLE_DRILL_DIA *1.5, -WALL_THICK])
		cube([BOX_HOLE_DRILL_DIA + WALL_THICK*2, BOX_HOLE_DRILL_DIA /2, WALL_THICK*2]);
	translate([-BOX_HOLE_DRILL_DIA/2 - WALL_THICK, 0, 0])
		cube([WALL_THICK, BOX_HOLE_DRILL_DIA *2, WALL_THICK]);
}

/// The clip is just a version of the retainer without the boss or detent spring and is for
// fixing in the opposite part of the box, so the latch arm slides into the gap
module Clip()
{
	RetainerHalf();
	mirror([0,1,0])
		RetainerHalf();
}

module Sprues()
{
	// Link the retainer and clip with two sprues, leave sprues in place until glued
	translate([BOX_HOLE_DRILL_DIA/2, BOX_HOLE_DRILL_DIA*1.5, 0])
		cube([BOX_HOLE_DRILL_DIA, WALL_THICK, WALL_THICK]);
	translate([BOX_HOLE_DRILL_DIA/2, -BOX_HOLE_DRILL_DIA*1.5 - WALL_THICK, 0])
		cube([BOX_HOLE_DRILL_DIA, WALL_THICK, WALL_THICK]);
	// Attach arm to door clip
	translate([BOX_HOLE_DRILL_DIA*1.75, -WALL_THICK/2, 0])
		cube([BOX_HOLE_DRILL_DIA, WALL_THICK, WALL_THICK]);
}

/// Utility methods
// obround: geometric racetrack (stadium) shape
module Obround(someLength, someWidth, someHeight)
{
    linear_extrude(someHeight)
    {
        square([someLength - someWidth, someWidth], center=true);
        translate([someLength / 2 - someWidth / 2, 0, 0])
            circle(r = someWidth / 2);
        translate([-someLength / 2 + someWidth / 2, 0, 0])
            circle(r = someWidth / 2);
    }
}

```


[User]
I'd like to design a parametric pen clip that can be attached to the back of a notebook. I need it to hold a pen securely and look nice.

Here are the specifications:

*   **Pen Clip:**
    *   The main body of the clip should be cylindrical. It should have a cutout on one side to allow a pen to be inserted. This cutout should be created by subtracting a rectangular prism from the cylinder.
    *   The cylinder needs to have a slightly larger outer radius than the pen so it holds it securely, and should be generated using the `cylinder()` primitive with `$fn=100` for smoothness.
    *   The cutout should be made with a `cube()` primitive to subtract from the cylinder.
    *   Two small cylinders should be added to the sides to help secure the pen with a small radius and placed just outside the main cylinder cutout. These should also use `$fn=100`.
*   **Base:**
    *   The base is a flat, rectangular shape that will be adhered to the notebook.
    *   It should be slightly wider than the clip and extend past the cylinder on the opposite side of the cutout.
    *   It should be created using a `cube()` primitive.
    *   I'd like to add text to the base. This should be extruded to create the 3D effect and centered on the base. The text should be created with the `text()` and `linear_extrude()` primitives.
*   **Fillets:**
    *   To give the base a more finished look I'd like to add fillets on the corners. These should be subtracted using `difference()` operation from the corners. The fillets should use a combination of `cube()` and `cylinder()` primitives, with `$fn=100` for the cylinders. These will need to be mirrored across the Z-axis to create the other fillets.

Here's how I want to parameterize it:

*   `Height`: The overall height of the pen clip cylinder
*   `Feet_size`: The length of the flat base
*   `Diameter`: The inner diameter of the cylinder that will hold the pen. Note: the outer diameter will be larger.
*   `Text`: The text to be displayed on the base.
*   `Text_Size`: The size of the text.

I'd prefer that the clip part of the design be created by using `difference()` and `translate()` to make sure the subtraction works and things are positioned correctly.

Could you generate the necessary OpenSCAD file(s) for this? I do not need to specify any library files, but you should make sure to use the `$fn` parameter when creating cylinders and to create rounded shapes. I will need to be able to edit all of the parametric values.

[Assistant]
Pen_clip.scad:
```
Height=20;
Feet_size=30;
Diameter=8;
Text="3D";
Text_Size=6;

my_object();

module my_object(){

// Creates the pen clip
translate([0,-Diameter/2,0]){
difference(){
    difference(){
        cylinder(h=Height,r=Diameter/2+0.8, center=true,$fn=100);
        cylinder(h=Height+1,r=Diameter/2, center=true,$fn=100);
    }
    translate([0,-2.75,-(Height+1)/2]) cube([20,5.5,Height+1]);
}
translate([sqrt((Diameter)/2*(Diameter)/2-2.75*2.75)+0.8,2.75,-Height/2]) color("yellow",0.5) cylinder(Height, r=0.8,$fn=100);
mirror([0,1,0]) translate([sqrt((Diameter)/2*(Diameter)/2-2.75*2.75)+0.8,2.75,-Height/2]) color("blue",0.5) cylinder(Height, r=0.8,$fn=100);
}

difference(){
// Create the base with the text
union(){
translate([-(Feet_size+sqrt((Diameter)/2*(Diameter)/2-(Diameter-1.6)/2*(Diameter-1.6)/2)),-0.8,-Height/2]) color("red",0.5)cube([Feet_size,0.8,Height]);

translate([-(Feet_size/2+Diameter/2),-0.8,0]) rotate([90,0,0]) linear_extrude(0.4) text(Text,font="font:style=bold", halign="center", valign = "center",size = Text_Size);
}

// Cuts the filet
union(){
translate([-(Feet_size+sqrt((Diameter)/2*(Diameter)/2-(Diameter-1.6)/2*(Diameter-1.6)/2))-0.01,-2.5,-Height/2-0.01])
difference(){
cube([2.5,5,2.5]);
translate([2.5,5.5,2.5]) rotate([90,0,0]) cylinder(h=6,r=2.5,$fn=100);
}
mirror([0,0,1])translate([-(Feet_size+sqrt((Diameter)/2*(Diameter)/2-(Diameter-1.6)/2*(Diameter-1.6)/2))-0.01,-2.5,-Height/2-0.01])
difference(){
cube([2.5,5,2.5]);
translate([2.5,5.5,2.5]) rotate([90,0,0]) cylinder(h=7,r=2.5,$fn=100);
}
}
}
}
```


[User]
I need a parametric OpenSCAD design for a mounting flange, kind of like the one described in the code you have, but I'd like it to be fully generated from scratch.  It's meant to attach a pipe or dowel to a flat surface.

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

**Central Tube:**

*   `TubeHeight`: The height of the central tube (the part that fits into the pipe or dowel).
*   `TubeInnerRadius`:  The inner radius of the central tube. If set to 0, it should be calculated based on the `TubeOuterRadius` and `TubeWallThickness`.
*   `TubeOuterRadius`: The outer radius of the central tube. If set to 0, it should be calculated from `TubeInnerRadius` and `TubeWallThickness`.
*   `TubeWallThickness`: The thickness of the tube wall.  This is ignored if both `TubeInnerRadius` and `TubeOuterRadius` are set.
*   `TubeTaper`: The amount the tube tapers outwards towards its base.
*   `TubePitch`:  The angle the tube makes with the flange, from -45 to 45 degrees.
*   `TubeHoleDepth`: The depth of the hole in the tube, measured from the top of the tube.

**Tube Collar:**

*   `CollarWidth`:  The radius expansion at the top of the tube (like a lip).
*  `CollarDepth`: The distance this expansion extends downward from the top of the tube.
*  `CollarChampferDepth`: The distance downward from the expansion to the point where the taper reaches the main tube width.

**Flange:**

*   `FlangeRadius`: The outer radius of the flange.
*   `FlangeThickness`: The thickness of the flange.

**Tube Pin Hole:**

*   `PinHoleRadius`:  The radius of a pinhole that goes through the side of the tube. If 0, no pin hole is added.
*   `PinHoleDepth`: The distance from the top of the tube to the center of the pinhole. If 0, the pinhole should default to half the `TubeHoleDepth`.
*   `PinHoleAngle`: The angle at which the pinhole is rotated around the tube's axis.

**Tube Supports:**

*   `SupportCount`: The number of supports between the tube and the flange. If 0, no supports will be added.
*   `SupportStartAngle`: The starting angle of the first support.
*  `SupportThicknessAngle`: The thickness of the support, measured as an angle.
*   `SupportInsetHeight`: How far the supports are inset from the top of the tube.
*   `SupportInsetFlange`: How far the supports are inset from the outer radius of the flange.

**Flange Mounting Holes:**

*   `HoleCount`: The number of mounting holes in the flange.
*   `HoleType`:  Type of hole,  0 = simple cylinder, 1 = bevelled, 2 = countersunk/recessed.
*  `HolePositionOffset`: An offset for the hole's center position from the radial position that should be used, so that multiple rows of holes are possible.
*   `HoleRadius1`: The inner radius of the mounting hole.
*   `HoleRadius2`: The outer radius of the mounting hole. If 0, it should default to the same value as  `HoleRadius1`.
*   `HoleHeadDepth`: The depth of the bevel or countersink (for bevelled and recessed holes).
*   `HoleStartAngle`: The starting angle for the first mounting hole.

I'd like all the dimensional parameters to be absolute values, ensuring that negative values are not used. And it would be useful to have assertions for making sure the Tube is properly defined, similar to the provided code.

I don't want to use any libraries for this. Could you please provide the code for a single .scad file?

[Assistant]
flangeRound.scad:
```

  //*************************
 //** Use SCAD Customizer **
//*************************

//TubeRadius = 36.5; pringles can

/* [Facets:] */
$fn = 60;

/* [Extrusion Correction:] */
EC = 0.5;

/* [Central Tube] */

// Distance from center line of hole to the origin
TubeHeight         = 30;
// inner radius of tube (if 0, is set from outer radius and thicknes
TubeInnerRadius    = 8;    // if 0 then = OuterRadius-TubeWallThickness
// Outer radius of tube (if 0, is set from outer radius and thicknes
TubeOuterRadius    = 0;    // If 0 then = InnerRadius+TubeWallThickness
// Thickness of tube wall, ignored if inner/outer radii are set
TubeWallThickness  = 3;
// Tube may taper outward toward it's base
TubeTaper          = 2;
// Angle Tube makes with the flange -45->45
TubePitch          = 30; // [-45:45]
// Depth of hole as measured fom top of tube
TubeHoleDepth      = 20;

/* [Tube Collar] */
// Radius expansion at top of tube
CollarWidth         = 2;
// Distance expansion linearly extends downward
CollarDepth         = 2;
// Distance downward for taper to tube width
CollarChampferDepth = 10;

/* [Flange] */
// Outer Radius of Flange
FlangeRadius       = 31;
// Thickness of Flange
FlangeThickness    = 4;

/* [Tube PinHole] */
// Size of Pin Hole through tube
PinHoleRadius      = 1;
// Distance from top of tube to pinhole
PinHoleDepth       = 0;
// Angle Pinhole is rotated
PinHoleAngle       = -60;

/* [Tube Supports] */
// Number of tube supports
SupportCount            = 6;
// Starting Angle of first support
SupportStartAngle       = 0;
// Support thickness measured as an angle
SupportThicknessAngle   = 4;
// Supports can be inset from the top of the tube
SupportInsetHeight      = 1;
// Supports can be inset from the outer radius of the base

SupportInsetFlange      = 2;

/* [Flange Mouting Holes] */
// Number of holes in plate
HoleCount          = 3;
// Hole Type
HoleType           = 1; // [0:2] = simple 1 = bevel, 2 = recessed
// Offset from center line
HolePositionOffset = -1;
// Iner Hole Radius
HoleRadius1        = 3;
// Outer Hole Radius
HoleRadius2        = 6;
// Depth of hole
HoleHeadDepth      = 2;
// Start angle
HoleStartAngle     = 60;

assert((abs(TubePitch) <= 45), "Tube Pitch must be between -45 and 45");

TIR1 = abs(TubeInnerRadius);
TOR1 = abs(TubeOuterRadius);
TWT1 = abs(TubeWallThickness);
TH   = abs(TubeHeight);
TTaper = abs(TubeTaper);
THD  = abs(TubeHoleDepth);
FR   = abs(FlangeRadius);
FThick = abs(FlangeThickness);

assert(((TIR1 + TOR1 + TWT1) > 0),
         "Tube Wall not specified");

assert(((TIR1 + TOR1) > 0),
         "TubeInnerWall or TubeOuterWall must be non-zero");

assert(((TIR1 + TWT1) > 0),
         "TubeInnerRadius can't ne computed");

assert(((TOR1 + TWT1) > 0),
         "TubeOuterRadius can't be computed");

assert((TOR1 != TIR1),
         "Tube hInternal & External radius may not be the same");

TWT = (TWT1 == 0) ? TOR1-TIR1 : TWT1;

TIR = (TIR1 == 0) ? TOR1 - TWT : TIR1;
TOR = (TOR1 == 0) ? TIR + TWT : TOR1;

PHD = (PinHoleDepth <= 0) ? THD/2 : PinHoleDepth;

Hpr = HolePositionOffset+(FR+TOR+TTaper)/2;

//------------------------------------
// Return a y axis rotation matrix
//
function MxRoty(a=0) = [[ cos(a), 0, -sin(a)],
                        [0      , 1,       0],
                        [ sin(a), 0,  cos(a)]];

Flange();

//------------------------------------
// Generate a flange support
//
module Flange() {

   MainBody();

//---------------------------------------------
// Generate the main body of the flange support
//
   module MainBody() {

      difference() {
         union() {
            Supports1();
            cylinder(h=FThick,r=FR-EC);

            rotate([0,TubePitch,0]) {
               translate([0,0,-TubeHeight])
               cylinder(h=TH*2,r1=TOR+TTaper-EC,r2=TOR-EC);
               Collar();
            }
         }

         rotate([0,TubePitch,0]) {
            #SidePinHole();
            CentralShaftHole();
         }

         FlangeHoles();
         mirror([0,0,1])
         cylinder(h=TH*2,r=FR*2);
      }

   }

//------------------------------------
// Gemerate upper tube collar if any
//
   module Collar() {

      if( (CollarWidth != 0 ) && (CollarDepth != 0)) {

         AR = (TIR+TOR)/2;
         rotate_extrude(angle = 360,convexity = 10) {
            polygon(points=[[AR,TH],
                            [TOR+CollarWidth,TH],
                            [TOR+CollarWidth,TH-CollarDepth],
                            [TOR,TH-CollarDepth-CollarChampferDepth],
                            [AR,TH-CollarDepth-CollarChampferDepth]]);
         }
      }

   }

   //---------------------------------
   // Remove the central shaft hole
   //
   module CentralShaftHole() {
      if (TH== THD) {
         translate([0,0,-.1])
         cylinder(h=THD+1,r=TIR+EC);
      } else {
         translate([0,0,(TH-THD)])
         cylinder(h=THD+1,r=TIR+EC);
      }
   }

   //-----------------------------------
   // Remove the side pin hole
   //
   module SidePinHole() {
      if (PinHoleRadius !=0) {
         translate([0,0,TH-PHD])
         rotate([0,90,PinHoleAngle])
         translate([0,0,-(TOR+TTaper+.1)])
         cylinder(h=(TOR+TTaper)*2+.1,r=PinHoleRadius+EC);
      }
   }

   //------------------------------
   // Remove the flange holes
   //
   module FlangeHoles() {

      HC = abs(HoleCount);
      HR1 = abs(HoleRadius1);
      HR2 =  (HoleRadius2 == 0) ? HR1 : abs(HoleRadius2);

      if (HoleType == 0) {
         for (i=[0:HC-1]) {

            rotate([0,0,i*360/HC+HoleStartAngle])
            translate([Hpr,0,-.5])
            cylinder(h=FThick+1,r=HR1+EC);

         }
      } else if (HoleType == 1) {
         for (i=[0:HC-1]) {

            rotate([0,0,i*360/HC+HoleStartAngle]) {
               translate([Hpr,0,-.5])
               cylinder(h=FThick+1,r=HR1+EC);
               translate([Hpr,0,FThick-abs(HoleHeadDepth)])
               cylinder(h=FThick+1,r=HR2+EC);
            }

         }
      } else {

         for (i=[0:HC-1]) {

            rotate([0,0,i*360/HC+HoleStartAngle])
            translate([Hpr,0,-.5])
            cylinder(h=FThick+1,r1=HR1+EC,r2=HR2+EC);

         }
      }

   }

   //---------------------------
   // Add tube supports
   //
   module Supports1() {
      SC = abs(SupportCount);

      if (SC != 0) {

         rm = MxRoty(TubePitch);

         for (i=[0:SC-1]) {

         ssa1 = i*360/SC+SupportStartAngle - SupportThicknessAngle;
         ssa2 = i*360/SC+SupportStartAngle + SupportThicknessAngle;

            ta = [(TOR-EC) * sin(ssa1),
                  (TOR-EC) * cos(ssa1),
                  TH-SupportInsetHeight];

            tb = [(TOR-EC) * sin(ssa2),
                  (TOR-EC) * cos(ssa2),
                  TH-SupportInsetHeight];

            t1a = ta*rm;
            t1b = tb*rm;

            f0a = [(TOR-EC) * sin(ssa1),
                 (TOR-EC) * cos(ssa1),
                 FThick-.1];

            f0b = [(TOR-EC) * sin(ssa2),
                   (TOR-EC) * cos(ssa2),
                 FThick-.1];

            f1a = [(FR-SupportInsetFlange) * sin(ssa1),
                 (FR-SupportInsetFlange) * cos(ssa1),
                 FThick-.1];

            f1b = [(FR-SupportInsetFlange) * sin(ssa2),
                 (FR-SupportInsetFlange) * cos(ssa2),
                 FThick-.1];

            polyhedron(points = [t1a,f0a,f1a,t1b,f0b,f1b],
                    faces =  [[0,1,2],[5,4,3],[0,2,5,3],[0,3,4,1],[1,4,5,2]],
                    convexity =10);

         }

      }

   }
}
```


[User]
I need a parametric OpenSCAD design for a case foot. It should be a truncated cone shape, with a flat bottom and a smaller circle inset into the top. This inset should also be a cylinder. There should be a counterbored hole going through the center of the foot for a screw. I'd like the option to have the screw counterbore flat or a beveled. Here are the dimensions I need to control:

*   **Overall Dimensions:**
    *   `Ft_BotR`: Radius of the bottom of the cone.
    *   `Ft_TopR`: Radius of the top of the cone.
    *   `Ft_H`: Height of the cone.

*   **Inset Dimensions:**
    *   `Ft_Inset_R`: Radius of the inset cylinder.
    *   `Ft_Inset_Dep`: Depth of the inset cylinder.

*   **Screw Hole Dimensions:**
    *   `Ft_Flat`: Boolean to control if the counterbore is flat (true) or beveled (false).
    *   `Ft_Sc_R`: Radius of the screw hole.
    *   `Ft_Sc_HeadR`: Radius of the screw head counterbore.
    *   `Ft_Sc_Inset`: Depth of the screw head counterbore.

I'd also like to have `$fn` as a configurable variable for the cylinder resolution, and `$EC` as an extra clearance.

Can you generate the OpenSCAD code for this? I don't need any external libraries used, please.

[Assistant]
CaseFoot.scad:
```
// Case Foot

/* [Misc] */
$fn = 30;       // [3:1:50]
$EC = 0.4;      // [0:.1:1]

/* [Dimensions] */
Ft_BotR        = 15;  // [2:.1:20]
Ft_TopR        = 20;  // [2:.1:20]
Ft_H           = 18;  // [2:.1:20]

/* [Inset] */
Ft_Inset_R     =  4;  // [2:.1:20]
Ft_Inset_Dep   =  3;  // [2:.1:20]

/* [Screw] */
Ft_Flat        = true;
Ft_Sc_R        =  4;  // [2:.5:10]
Ft_Sc_HeadR    =  7;  // [2:.1:15]
Ft_Sc_Inset    =  4;  // [2:.1:20]

//mirror([0, 0, 1])     // rotate for better print/view
Foot();

//---------------------
// Generate Foot
//
module Foot() {
   translate([0,0,-Ft_H])
   difference() {
      cylinder(r1=Ft_BotR, r2=Ft_TopR, h=Ft_H);

      translate([0,0,-.01])
      cylinder(r=Ft_BotR-Ft_Inset_R, h=Ft_Inset_Dep+.01);

      if (!Ft_Flat)
      translate([0,0,Ft_Sc_Inset+Ft_Inset_Dep-.01])
      cylinder(r1=Ft_Sc_HeadR+$EC,r2=0,h=Ft_Sc_HeadR+$EC);

      cylinder(r=Ft_Sc_HeadR+$EC, h=Ft_Sc_Inset+Ft_Inset_Dep);

      cylinder(r=Ft_Sc_R   +$EC, h=Ft_H+.01);
   }
}
```


[User]
Hey, I'm looking to create a custom angle gauge, and I'd like it to be parametric so I can easily adjust the dimensions and type. I need both inside and outside versions.

Here's what I envision:

**Core Functionality:**

*   **Inside/Outside Selection:** I want to be able to choose whether it's an inside or outside angle gauge using a dropdown with the values 0 (for outside) and 1 (for inside).
*   **Angle Adjustment:**  A parameter to set the angle to be measured, anywhere from 1 to 179 degrees, with a 0.01 degree increment.
*   **Length of Measuring Edges:** A parameter to set the length of the gauge's measuring edges in mm.
*   **Width of Outside Gauge Arms:** A parameter for setting the width of the arms of the *outside* angle gauge.
*   **Thickness:**  A parameter to control the thickness of the gauge.
*   **Relief Hole:** A circular relief hole should be added to the inside corner of inside gauges and to the outside corner of outside gauges.  The diameter of the hole should be a percentage of the arm Width parameter (e.g., if width is 10mm, a 20% relief hole would be 2mm in diameter). This percentage should be adjustable.
*   **Text Label:**  The angle value should be displayed on the gauge with a text label, including a degree symbol. It should be centered on the outside gauge and left/right aligned on the inside gauge depending on whether the angle is <45, <90 or >90, respectively.
*   **Inside Fan Option:** For inside angle gauges I'd like an "Inside Fan" parameter (with values 0 or 1) that makes inside gauges more fan-shaped for angles close to 180 degrees. This makes them more useful. This should also be automatically triggered for extreme obtuse angles.

**Specific Design Notes:**

*   The inside gauge should be created as the negative space between two lines at the requested angle.
*   The relief holes should be subtracted from the body of each gauge, and should be centered on the inside angle of the inside gauge and outside angle of the outside gauge.
*   Text label should be the same height as 1/2 the arm width parameter, even when creating inside gauges.
* The position of the text label on the inside gauge is determined by which side the label will be displayed on. It should be moved further in to the vertex of the angle if it is placed on the inside of the angle and placed closer to the end if it is on the outside of the angle.
* The text label on the outside gauge should always be centered at the vertex.
* The relief holes should be slightly offset when created so that they do not generate errors.

I'm not using any specific libraries here and expect to be able to generate this solely from standard OpenSCAD primitives.

[Assistant]
CustomAngle.scad:
```
// Zero (0) yields outside gauge.  One (1) creates an inside gauge.
Inside          = 0;       // [0,1]
// Angle to be measured
Angle           = 22.5;    // [1:0.01:179]
// Length of measuring edges (mm)
Length          = 100;
// Width of outside angle gauge arms (mm)
Width           = 10;
// Thickness (height) of gauge (mm)
Thickness       = 5;
// Relief hole diameter as a percentage of Width setting
ReliefPct       = 20;  // [0:5:100]
// Text height is always 1/2 of Width, even on inside angle gauges
DummyVarToHoldComment = 0;       // [0,1]
// Makes inside gauges fan shaped rather than triangle shaped
InsideFan       = 0;       // [0,1]

ReliefHole      = Width*ReliefPct/100;
HoleMovement    = ReliefHole/4;
AngleText       = str(Angle,chr(176));
TextFont        = str("") ;
AutoFan         = Angle>170&&InsideFan==false;

if (Inside)
  Inside();
else
   Outside();

module Inside() {
  Epsilon   = .001;
  Halign    = Angle<45?"right":Angle<90?"center":"left";
  Hdist     = Angle<45?Length-3:Angle<90?Length/2:ReliefHole;
  InsideFan = AutoFan?true:InsideFan;

  if(AutoFan) echo("<b>Automatically rendering in FAN mode due to extreme obtuse angle");
  difference() {
    if (InsideFan)
      rotate_extrude(angle=Angle,$fn=180) translate([ReliefHole/2,0,0])   square(size=[Length-ReliefHole/2,Thickness], center=false);
    else
      hull(){
        union() {
          cube(size=[Length,Epsilon,Thickness]);
          rotate([0,0,Angle]) cube(size=[Length,Epsilon,Thickness]);
        }
      }
    translate([0,0,-1]) cylinder(h=Thickness+2, d=ReliefHole, $fn=80);
    translate([Hdist,0.5,Thickness-1])
        linear_extrude(height=1.1,center=false)
        text(text=AngleText,font=TextFont,size=Width/2,halign=Halign,valign="bottom",spacing=1,direction="ltr");
  }
}

module Outside() {
  difference() {
    union() {
      hull() {
        translate([-Width,-Width,0])  cube(size=[Width,Width,Thickness], center=false);
        difference() {
          rotate([0,0,Angle])  cube(size=[Width,Width,Thickness], center=false);
          translate([0,0,-1])  cube(size=[Width,2*Width,Thickness+2], center=false);
        }
      }
      translate([0,-Width,0])  cube(size=[Length,Width,Thickness]);
      rotate([0,0,Angle])  cube(size=[Length,Width,Thickness]);
    }
    translate([Length/2,-Width/2,Thickness-1]) linear_extrude(height=1.1,center=false)
        text(text=AngleText,font=TextFont,size=Width/2,halign="center",valign="center",spacing=1,direction="ltr");
    translate([HoleMovement*cos(Angle/2),HoleMovement*sin(Angle/2),-1])
        cylinder(h=Thickness+2, d=ReliefHole, center=false, $fn=24);
  }
}

```


[User]
Hey, I'm looking to create a parametric 3D model of a pumpkin, and I'd like to have it be configurable with some specific parameters. I need it to be a modular design, so I can generate different pumpkins from one file. I'd like the pumpkin body to be made of vertical segments, with indentations between them both on the sides and top/bottom. The whole thing needs to be parametric, so the number of segments, the depth of those indentations, and the overall width and height are all configurable.

I’d also like to be able to choose whether or not to flatten the bottom of the pumpkin, and if I do, how much to slice off from the bottom. And finally, I need a parametric stem, where I can set the number of sides, the height, width at the top and bottom, how much the width changes between the base and the top (a sort of transition height), its rotation, and the amount of "curl" at the top.

Could you generate the OpenSCAD code for this, and I think I would like to have the following parameters:

**[Pumpkin]**
*   `pumpkin_width`: The overall width of the pumpkin.
*   `pumpkin_height`: The overall height of the pumpkin.
*   `segment_count`: The number of vertical segments that make up the pumpkin.
*   `pumpkin_top_dent`: The depth of the indentation at the top and bottom of the pumpkin segments.
*   `pumpkin_side_dent`: The depth of the indentation between the vertical segments.
*    `stem`: A boolean to enable or disable the stem.

**[Bottom]**
*   `flat_bottom`: A boolean to control whether to flatten the bottom of the pumpkin.
*   `slice_bottom`: The amount of material to slice off the bottom, used in conjunction with `flat_bottom`.

**[Stem]**
*   `stem_sides`: The number of sides on the stem.
*   `stem_height`: The height of the stem.
*   `stem_width`: The width of the stem at the top.
*   `stem_width_bottom`: The width of the stem at the bottom.
*   `stem_transition`:  Where the transition from `stem_width_bottom` to `stem_width` is complete, as a ratio of `stem_height`.
*   `stem_twist`: The angle through which the stem is twisted.
*   `stem_curl`: The amount of curl at the top of the stem.

Also, please make sure there’s a parameter for `fn` to control the resolution of curves. And I don't need any external libraries for this one. Thanks!

[Assistant]
pumpkin.scad:
```
/* [Pumpkin] */
// Width of the pumpkin
pumpkin_width = 40; // [0:0.5:400]

// Height of the pumpkin
pumpkin_height = 30; // [0:0.5:400]

// Number of pumpkin segments
segment_count = 10; // [1:1:100]

// Top (and bottom) indentation depth
pumpkin_top_dent = 0.4; // [0:0.01:0.5]

// Side indentation depth
pumpkin_side_dent = 1.8; // [1:0.01:4]

// Whether to have a stem
stem = true;

/* [Bottom] */
// Whether to flatten bottom
flat_bottom = true;

// How much material to remove at bottom
slice_bottom = 0.05; // [0:0.01:0.99]

/* [Stem] */
// Number of sides on stem
stem_sides = 5; // [3:1:10]

// Height of the stem
stem_height = 16; // [1:0.1:100]

// Width of stem at top
stem_width = 10; // [0.1:0.1:100]

// Width of stem at bottom
stem_width_bottom = 15; // [0.1:0.1:100]

// Where the transition between small and large is complete
stem_transition = 0.25; // [0.01:0.01:1]

// Angle through which stem is rotated
stem_twist = 160; // [0:1:360]

// Curl given to stem
stem_curl = 3; // [0:0.1:50]

/* [Util] */
// Resolution
fn = 30; // [5:1:200]

/* [Hidden] */
p_rad = pumpkin_width / 2;

$fn = fn;

module p_segment(r, h, a, d, td) {
    w = (r - td) * 2;
    maxw = max(w, h);
    real_a = a * d;
    off = r * td;
    difference() {
        scale([1, 1/d])
        rotate([0, 0, -(real_a / 2)])
        rotate_extrude(angle = real_a)
            difference() {
                translate([off, 0]) scale([1, h/(2 * (r - off))]) circle(r - off);
                translate([-maxw, -maxw / 2]) square(size = maxw);
            }
        rotate([0, 0, -90 + a/2]) translate([-w, 0, -h/2]) cube([w, w, h]);
        rotate([0, 0, -90 - a/2]) translate([0, 0, -h/2]) cube([w, w, h]);
    }
}

module pumpkin(r, h, segments, side_depth, top_depth) {
    da = 360 / segments;
    for (i = [0:segments - 1]) {
        angle = da * i;
        rotate([0, 0, angle]) p_segment(r, h, da, side_depth, top_depth);
    }
}

module p_stem(w, bw, h, s, t, a, c) {
    h1 = h * t;
    h2 = h - h1;
    a1 = a * t;
    a2 = a - a1;
    linear_extrude(height = h1, twist = a1, scale=w / bw) {
        circle($fn = s, r = bw / 2);
    }
    rotate([0, 0, -a1])
    translate([-c, 0, h1])
    linear_extrude(height = h2, twist = a2) {
        translate([c, 0, 0]) circle($fn = s, r = w / 2);
    }
}

difference() {
    translate([0, 0, -(flat_bottom ? 0.01 + (slice_bottom / 10 * pumpkin_height): 0)])
    union() {
        translate([0, 0, pumpkin_height / 2])
            pumpkin(p_rad, pumpkin_height, segment_count, pumpkin_side_dent, pumpkin_top_dent);
        if (flat_bottom) {
            center_r = pumpkin_width / 2 * pumpkin_top_dent;
            cylinder(r = center_r, h=pumpkin_height / 2);
        }
        if (stem) {
            translate([0, 0, pumpkin_height - (pow(pumpkin_top_dent, 2) * (pumpkin_width / 2))])
            p_stem(stem_width / 2, stem_width_bottom / 2, stem_height, stem_sides, stem_transition, stem_twist, stem_curl);
        }
    }
    if (flat_bottom) {
        translate([-pumpkin_width / 2, -pumpkin_width / 2, -pumpkin_height])
        cube([pumpkin_width, pumpkin_width, pumpkin_height]);
    }
}
```


[User]
Hi, I'm looking for help designing a parametric OpenSCAD model for a universal bowl adapter. I've seen some basic ones but want something more flexible. I need to be able to specify the size of both the upper and lower stems, as well as the stem height, wall thickness, and taper. I also want to be able to control whether the top and bottom are male or female, so I can have adapters that go from male to male, female to female, etc.

The stem itself should have a slight error to ensure a good fit, say, 0.2mm larger than the nominal size. The taper, if present, should only be on the outside of the male part and the inside of the female part. I need control over the taper amount as well. I also need the top and bottom connectors to have a 2mm wall thickness.

To clarify, by "stem" I'm referring to the cylindrical part that goes inside a bong or bowl, and the overall adapter would connect two different sized stems together.

Ideally, I would like a single .scad file that contains all the parametric definitions, and then separate module definitions for the following parts: `STEM_MALE`, `STEM_FEMALE`, `STEM_JOINT`, and `STEM_CONVERTER`. The `STEM_JOINT` module would be what connects two stems of different sizes together, which would also have a wall thickness. The `STEM_CONVERTER` module would assemble these parts together into the final adapter.

I want to be able to configure the final converter's dimensions, as well as the gender of the top and bottom connections separately. I'm not too familiar with OpenSCAD, so make sure the model is robust and easy to use, also, if you could use some common values for `$fa` and `$fs` to smooth the cylinders, that would be great. I think that $fa=6; and $fs=.2; would be good for the cylinder creation.

Please let me know if you have any questions.

[Assistant]
UniversalBowlAdapter.scad:
```
/* [Stem Settings] */
// Size of upper stem, in mm
UPPER_STEM_SIZE=14; // [8:1:25]

// Size of lower stem, in mm
LOWER_STEM_SIZE=14; // [8:1:25]

/* [Stem Geometry] */
STEM_HEIGHT=24;

// The top width of the stem is usually .10 to .40 wider than the actual size
STEM_SIZE_ERROR=0.20;

// Difference from top of stem to bottom
STEM_TAPER=2;

STEM_WALL_THICKNESS=2;

/* [Hidden] */

// https://forum.openscad.org/Round-shapes-created-in-openscad-are-not-smooth-but-hexigonal-tp24407p24411.html
// $fa is always 6, $fs half extrusion width
// Most nozzles are 0.4 mm, so $fs is 0.2 mm
$fa=6;
$fs=.2;

module STEM_MALE(SIZE) {
    NOMINAL_SIZE = SIZE + STEM_SIZE_ERROR;

    difference() {
        // Outer Geometry
        cylinder(h = STEM_HEIGHT, d1 = NOMINAL_SIZE-STEM_TAPER, d2 = NOMINAL_SIZE, center = true);

        // Inner Geometry, same as outer geometry but diameter is STEM_WALL_THICKNESS smaller
        cylinder(h = STEM_HEIGHT, d1 = (NOMINAL_SIZE-STEM_TAPER)-STEM_WALL_THICKNESS, d2 = NOMINAL_SIZE-STEM_WALL_THICKNESS, center = true);
    }
}

// STEM_FEMALE is identical to STEM_MALE except:
// - union instead of difference
// - STEM_WALL_THICKNESS is added, not subtracted
module STEM_FEMALE(SIZE, TAPER_OUTER_GEOMETRY = false) {
    NOMINAL_SIZE = SIZE + STEM_SIZE_ERROR;

    // Lower diameter is the same as upper diameter, unless TAPER_OUTER_GEOMETRY
    OUTER_GEOMETRY_UPPER_DIAMETER=NOMINAL_SIZE+STEM_WALL_THICKNESS;
    OUTER_GEOMETRY_LOWER_DIAMETER=OUTER_GEOMETRY_UPPER_DIAMETER;
    if (TAPER_OUTER_GEOMETRY) {
        OUTER_GEOMETRY_LOWER_DIAMETER=(NOMINAL_SIZE-STEM_TAPER)+STEM_WALL_THICKNESS;
    }

    difference() {
        // Outer Geometry
        cylinder(h = STEM_HEIGHT, d1 = OUTER_GEOMETRY_LOWER_DIAMETER, d2 = OUTER_GEOMETRY_UPPER_DIAMETER, center = true);

        // Inner Geometry, same as outer geometry but diameter is STEM_WALL_THICKNESS wider
        cylinder(h = STEM_HEIGHT, d1 = NOMINAL_SIZE-STEM_TAPER, d2 = NOMINAL_SIZE, center = true);
    }
}

// STEM_JOINT joins a STEM_FEMALE up top to a STEM_MALE on the bottom
module STEM_JOINT(TOP_SIZE, BOTTOM_SIZE) {
    // Have to do some fuckery to get around variable scoping rules
    // If top diameter is bigger, then TOP_SIZE and BOTTOM_SIZE need to be swapped
    TOP_SIZE_TEMP = TOP_SIZE;
    BOTTOM_SIZE_TEMP = BOTTOM_SIZE;
    TOP_SMALLER = TOP_SIZE < BOTTOM_SIZE;

    TOP_SIZE = TOP_SMALLER ? BOTTOM_SIZE_TEMP-STEM_WALL_THICKNESS : TOP_SIZE_TEMP;
    BOTTOM_SIZE = TOP_SMALLER ? TOP_SIZE_TEMP : BOTTOM_SIZE_TEMP;

    TOP_DIAMETER = (TOP_SIZE+STEM_SIZE_ERROR)+STEM_WALL_THICKNESS;
    BOTTOM_DIAMETER = (BOTTOM_SIZE+STEM_SIZE_ERROR)-STEM_WALL_THICKNESS;

    difference() {
        cylinder(h = STEM_WALL_THICKNESS, d=TOP_DIAMETER);

        cylinder(h = STEM_WALL_THICKNESS, d=BOTTOM_DIAMETER);
    }
}

module STEM_CONVERTER(TOP_SIZE, BOTTOM_SIZE) {
    union() {
        translate([0, 0, STEM_HEIGHT+STEM_WALL_THICKNESS])
            STEM_FEMALE(TOP_SIZE);

        translate([0, 0, STEM_HEIGHT/2])
            STEM_JOINT(TOP_SIZE, BOTTOM_SIZE);

        STEM_MALE(BOTTOM_SIZE);
    }
}

STEM_CONVERTER(UPPER_STEM_SIZE, LOWER_STEM_SIZE);
```


[User]
I'd like a clock face generator. The key feature I'm looking for is the ability to customize the appearance of the clock's face, so the design should be fully parametric.

Specifically, I want to control the following aspects:

1.  **Overall Dimensions:** I need to be able to adjust the `faceDiameter`.
2.  **Scale Rings:** The clock face should have inner and outer scale rings. I want to control the `scaleThickness` (total thickness of both rings), `scaleRingThickness` (thickness of a single ring), and `scaleHeight`.
3.  **Tick Marks:** I want to independently control the height of the hour and minute tick marks using `hourTicksHeight` and `minuteTicksHeight` respectively. I also need to adjust their lengths, using parameters `hourTicksLength` and `minuteTicksLength`.
4.  **Digits:** I want to be able to choose between Arabic and Roman numerals (or even turn them off altogether), controlled by a `digitsType` parameter. I'd also need to be able to adjust the `fontSize`, `fontType`, and `fontSpacing`, along with the `fontHeight`, for digits. The `digitsRadius` should also be controllable to position digits with respect to ticks.
5.  **Resolution:** I'd like to be able to control the rendering resolution, which should default to something reasonable.

I'd like it all in a single file for easy use. I'd prefer not to use any external libraries for this particular design. Please generate the complete OpenSCAD file. I want the design to work as a 3D printable model.

[Assistant]
Classic_Clock_Face.scad:
```
//--------------------------------------------------
// Classic Clock Face v1.0 by Serg Zabota
//--------------------------------------------------

resolution = 100;
faceDiameter = 150;
scaleThickness = 8; // include both rings
scaleRingThickness = 2;
scaleHeight = 2;
hourTicksHeight = 3;
minuteTicksHeight = 2;
digitsType = 1; // [1:Arabic, 2:Roman]
//digitsType = 2; // [1:Arabic, 2:Roman]
fontType = "hebrew"; // "hebrew" for cool Arabic Digits
//fontType = "BatangChe"; // "BatangChe" for Roman Digits
fontSize = 12;
fontSpacing = 0.85; // 0.85 for Arabic hebrew
//fontSpacing = 0.5; // 0.5 for Roman BatangChe
fontHeight = 4;

minuteTicksLength = scaleThickness;
hourTicksLength = scaleThickness+scaleRingThickness*2;
digitsRadius = faceDiameter*0.5-hourTicksLength-fontSize*0.8;

digits = digitsType==1 ? ["12", " 11", " 10", "9 ", "8", "7", "6", "5", "4", "3", "2", "1"] : // Use spaces to align numbers
                         ["XII", "XI ", "X ", "IX  ", "VIII ", "VII", "VI", "V ", "IIII ", "III ", "II ", "I "];
//--------------------------------------------------

$fn=resolution;

union(){
  linear_extrude(height=scaleHeight){
    difference() { // Outer ring
      circle(d=faceDiameter );
      circle(d=faceDiameter-scaleRingThickness*2);
    }
    difference() { // Inner ring
      circle(d=faceDiameter-scaleThickness*2+scaleRingThickness*2);
      circle(d=faceDiameter-scaleThickness*2);
    }
  }
  for(i=[1:12]){ // Hour ticks
    rotate([0,0,i*30])
      translate([faceDiameter*0.5-hourTicksLength*0.5,0,hourTicksHeight*0.5])
        cube([hourTicksLength, scaleRingThickness, hourTicksHeight], true);
  }
  for(i=[1:60]){ // Minute ticks
    rotate([0,0,i*6])
      translate([faceDiameter*0.5-minuteTicksLength*0.5,0,minuteTicksHeight*0.5])
        cube([minuteTicksLength, scaleRingThickness*0.5, minuteTicksHeight], true);
  }
}

if (digitsType==1) { // Arabic Digits
  linear_extrude(height=fontHeight)
    for (i=[0:11]) {
      translate([digitsRadius*cos((i+3)*30),digitsRadius*sin((i+3)*30),0])
        text(text=digits[i],
          valign="center",
          halign="center",
          size=fontSize,
          font=fontType,
          spacing=fontSpacing);
    }
}
else { // Roman Digits
  linear_extrude(height=fontHeight)
    for (i=[0:11]) {
      rotate([0,0,(i+3)*30])
        translate([digitsRadius,0,0])
          rotate([0,0,-90])
            text(text=digits[i],
              valign="center",
              halign="center",
              size=fontSize,
              font=fontType,
              spacing=fontSpacing);
    }
}

```


[User]
Hey, I'm looking to create a customizable hanger design using OpenSCAD, and I'd love some help getting started. I've got a general idea, but I need it to be parametric so I can adjust different aspects of the design easily.

The hanger will essentially be two curved arms connected at a single point. Each arm is created by rotating a rectangle around an axis. Here are the specific parameters I need to control:

*   **width:** The width of the rectangular cross-section of the arm.
*   **thickness:** The thickness of the rectangular cross-section of the arm.
*   **radius1:** The radius of the curve for the first arm.
*   **radius2:** The radius of the curve for the second arm.
*   **angle1:** The angle in degrees of the first curved arm.
*   **angle2:** The angle in degrees of the second curved arm.

The first arm should be created by rotating a rectangle around an axis located at a specific radius (radius1). The second arm should be mirrored across the x and y axis, and it's based on radius2. Both arms should have rectangular cross-sections defined by the width and thickness parameters.

I'm not sure how to best connect the arms in the model or how to handle the transformations. I would like a single SCAD file which has all the parameters at the top and then uses them to generate the hanger. Could you generate the necessary OpenSCAD code for this? No libraries are needed.

[Assistant]
hanger.scad:
```
width = 10; // mm width
radius1 = 10; // mm first radius
radius2 = 15; // mm second radius
thickness = 4; // mm thickness
angle1 = 300; // degrees for first radius
angle2 = 280; // degrees for second radius

module rectangle(width, thickness){
    square([thickness, width]);
}

rotate_extrude(angle=angle1)
    translate([radius1-thickness/2, 0])
        rectangle(width, thickness);
translate([radius2+radius1, 0])
    mirror([1, 0, 0]) mirror([0, 1, 0])
        rotate_extrude(angle=angle2)
            translate([radius2-thickness/2, 0])
                rectangle(width, thickness);
```