[User]
Hey there, I'm looking to create a customizable storage box with a lid, and I'd like to use OpenSCAD to make it parametric. I've got a basic idea in mind, but I need help getting the code set up.

Essentially, I want a box with a grid of cubbies inside. I need to be able to control the number of cubbies wide and deep, as well as the size of each cubby, and their height. I'd also like to adjust the thickness of the internal dividers and the outer walls of the box.

The lid should fit into the box with an overhang, and I'd like to customize the thickness of the lid and the dimensions of this overhang. Also, it'd be great to have a little notch on the lid for easy opening. And, most importantly, I want a parameter for tolerance to adjust the fit of the lid.

For the specific parameters, I’m thinking along these lines:
*   **Rendering:** Ability to render box, lid, or both; option to render lid in place for print-in-place with supports (though this might be experimental); plus animation option for the lid.
*   **Grid:** Number of cubbies wide (`grid_size_x`), number of cubbies deep (`grid_size_y`).
*   **Cubby:** Size of each cubby (`cubby_size`), height of each cubby (`cubby_height`).
*   **Dividers:** Thickness of dividers (`divider_thickness`), gap between dividers (`divider_gap`).
*   **Walls:** Thickness of the outer walls of the box (`wall_thickness`).
*   **Lid:** Lid thickness (`lid_thickness`), overhang width (`overhang_width`), overhang height (`overhang_height`), tolerance (`tolerance`)
*   **Notch:** Length of notch (`notch_x`), width of notch (`notch_y`), depth of notch (`notch_thickness`), distance of notch from the edge of the lid (`notch_gap`)

I’m hoping this can all be done in a single .scad file, if possible. I don’t need any special libraries. Let me know if you need any other info. I'm looking forward to seeing what you come up with!

[Assistant]
EasyShapeOrganizer_v1.1.scad:
```
/* [Rendering:] */
// Boolean to trigger rendering box
render_box = true;
// Boolean to trigger rendering lid
render_lid = true;
// Boolean to render lid in place
render_in_place = false;

/* [Basic Sizing:] */
// Number of cubbies wide
grid_size_x = 4;
// Number of cubbies deep
grid_size_y = 3;
// The size of each cubby in mm
cubby_size = 25;
// The height of the cubbies in mm
cubby_height = 25;
// Divider gap in mm (0 to ignore)
divider_gap = 10;

/* [Detailed Sizing:] */
// The width in mm of the internal dividers
divider_thickness = 2;
// The thickness in mm of the entire box
wall_thickness = 3;
// The thickness of the lid in mm (height)
lid_thickness = 1.5;
// The amount of thickness to cut into the walls for the lid in mm
overhang_width = 1.5;
// The additional height for the top of the box in mm above the lid
overhang_height = 1.5;
// Tolerance for sliding fit
tolerance = 0.20;

/* [Lid Notch:] */
// Length of notch on lid in mm
notch_x = 25;
// Width of notch on lid in mm
notch_y = 2;
// Depth of notch engraving on lid in mm
notch_thickness = 0.5;
// Distance for notch from edge of lid in mm
notch_gap = 5;

/* [Extra:] */
// Boolean to animate the rendering
animate = false;

// Perform checks
assert(grid_size_x > 0, "Must be at least 1 cubby wide");
assert(grid_size_y > 0, "Must be at least 1 cubby deep");
assert(cubby_size > 0);
assert(cubby_height > 0);
assert(divider_gap < cubby_size, "Divider gap must be smaller than cubby");
assert(wall_thickness > 0);
assert(overhang_width < wall_thickness, "Overhang width for lid must be smaller than wall width");
assert(tolerance < lid_thickness, "Tolerance must be smaller than the lid");
assert(tolerance < overhang_width, "Tolerance must be smaller than the overhang width");
assert(divider_gap < cubby_size, "Divider gap must be smaller than the cubby size");

module box(grid_size_x, grid_size_y, cubby_size, cubby_height,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance, divider_gap) {

    // Derived Parameters
    internal_size_x = grid_size_x * cubby_size + (grid_size_x - 1) * divider_thickness;
    internal_size_y = grid_size_y * cubby_size + (grid_size_y - 1) * divider_thickness;
    outer_size_x = internal_size_x + 2 * wall_thickness;
    outer_size_y = internal_size_y + 2 * wall_thickness;
    box_height = cubby_height + lid_thickness + overhang_height;
    lid_size_x = internal_size_x + 2 * overhang_width;
    lid_size_y = internal_size_y + 2 * overhang_width;

    // Outer Box with Cubbies
    difference() {
        // Main box
        cube([outer_size_x, outer_size_y, box_height]);

        // Subtract cubbies
        for (x = [0:grid_size_x-1]) {
            for (y = [0:grid_size_y-1]) {
                translate([wall_thickness + x * (cubby_size + divider_thickness),
                           wall_thickness + y * (cubby_size + divider_thickness),
                           wall_thickness]) {
                    cube([cubby_size, cubby_size, box_height]);
                }
            }
        }

        // Subtract walls above cubbies
        translate([wall_thickness, wall_thickness, cubby_height])
        cube([internal_size_x, internal_size_y, box_height-cubby_height]);

        // Subtract lid (with tolerance)
        translate([wall_thickness-overhang_width, 0, cubby_height])
        lid(grid_size_x, grid_size_y, cubby_size,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance,
            notch_x, notch_y, notch_thickness, notch_gap);

        // Subtract spot above lid on near-side
        translate([wall_thickness, 0, cubby_height])
        cube([internal_size_x, wall_thickness, overhang_height+lid_thickness]);

        // Divider gaps
        if(divider_gap > 0) {
            divider_add = (cubby_size / 2) - (divider_gap / 2 );
            for (x = [0:grid_size_x-1]) {
                translate([wall_thickness + x * (cubby_size + divider_thickness) + divider_add,
                           wall_thickness,
                           wall_thickness]) {
                    cube([divider_gap, internal_size_y, box_height]);
                }
            }
            for (y = [0:grid_size_y-1]) {
                translate([wall_thickness,
                           wall_thickness + y * (cubby_size + divider_thickness) + divider_add,
                           wall_thickness]) {
                    cube([internal_size_x, divider_gap, box_height]);
                }
            }
        }
    }
}

module lid(grid_size_x, grid_size_y, cubby_size,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance,
            notch_x, notch_y, notch_thickness, notch_gap) {

    // Derived Parameters for the Lid
    internal_size_x = grid_size_x * cubby_size + (grid_size_x - 1) * divider_thickness;
    internal_size_y = grid_size_y * cubby_size + (grid_size_y - 1) * divider_thickness;
    lid_size_x = internal_size_x + 2 * overhang_width + 2*tolerance;
    lid_size_y = internal_size_y + overhang_width + wall_thickness + tolerance;

    // Lid with notch
    difference() {
        // Create lid
        cube([lid_size_x, lid_size_y, lid_thickness + 2*tolerance]);

        color("orange")
        translate([lid_size_x / 2, notch_gap, lid_thickness - notch_thickness / 2])
        cube([notch_x, notch_y, notch_thickness], center = true);

        color("red")
        translate([0, lid_size_y/2, lid_thickness + 2*tolerance])
        rotate([0, 45, 0])
        cube([overhang_width, lid_size_y, max(lid_thickness, overhang_width)], center=true);

        color("green")
        translate([lid_size_x, lid_size_y/2, lid_thickness + 2*tolerance])
        rotate([0, -45, 0])
        cube([overhang_width, lid_size_y, max(lid_thickness, overhang_width)], center=true);

        color("blue")
        translate([lid_size_x/2, lid_size_y, lid_thickness + 2*tolerance])
        rotate([45, 0, 0])
        cube([lid_size_x, overhang_width, max(lid_thickness, overhang_width)], center=true);

    }
}

if (render_box) {
    // Create the Box
    box(grid_size_x, grid_size_y, cubby_size, cubby_height,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance, divider_gap);
}

if (render_lid) {
    internal_size_y = grid_size_y * cubby_size + (grid_size_y - 1) * divider_thickness;
    outer_size_y = internal_size_y + 2 * wall_thickness;

    lid_trans = (render_box && render_lid) ?
        render_in_place ?
        animate ?
            [wall_thickness - overhang_width + tolerance,
                -outer_size_y * abs(cos(($t * 100) * PI)),
                cubby_height + tolerance] :
            [wall_thickness - overhang_width + tolerance, 0, cubby_height + tolerance] :
            [grid_size_x * cubby_size + 10 + wall_thickness * 2 + 3, 0, 0] :
            [0, 0, 0];

    translate(lid_trans)
    lid(grid_size_x, grid_size_y, cubby_size,
        divider_thickness, wall_thickness, lid_thickness,
        overhang_width, overhang_height, 0,
        notch_x, notch_y, notch_thickness, notch_gap);
}
```
EasyShapeOrganizer.scad:
```
/* [Rendering:] */
// Boolean to trigger rendering box
render_box = true;
// Boolean to trigger rendering lid
render_lid = true;
// Boolean to render lid in place
render_in_place = false;

/* [Basic Sizing:] */
// Number of cubbies wide
grid_size_x = 3;
// Number of cubbies deep
grid_size_y = 2;
// The size of each cubby in mm
cubby_size = 25;
// The height of the cubbies in mm
cubby_height = 25;
// Divider gap in mm (0 to ignore)
divider_gap = 10;

/* [Detailed Sizing:] */
// The width in mm of the internal dividers
divider_thickness = 1;
// The thickness in mm of the entire box
wall_thickness = 5;
// The thickness of the lid in mm (height)
lid_thickness = 2;
// The amount of thickness to cut into the walls for the lid in mm
overhang_width = 1.5;
// The additional height for the top of the box in mm above the lid
overhang_height = 3;
// Tolerance for sliding fit
tolerance = 0.25;

/* [Lid Notch:] */
// Length of notch on lid in mm
notch_x = 25;
// Width of notch on lid in mm
notch_y = 2;
// Depth of notch engraving on lid in mm
notch_thickness = 0.5;
// Distance for notch from edge of lid in mm
notch_gap = 5;

/* [Extra:] */
// Boolean to animate the rendering
animate = false;

// Perform checks
assert(grid_size_x > 0, "Must be at least 1 cubby wide");
assert(grid_size_y > 0, "Must be at least 1 cubby deep");
assert(cubby_size > 0);
assert(cubby_height > 0);
assert(divider_gap < cubby_size, "Divider gap must be smaller than cubby");
assert(wall_thickness > 0);
assert(overhang_width < wall_thickness, "Overhang width for lid must be smaller than wall width");
assert(tolerance < lid_thickness, "Tolerance must be smaller than the lid");
assert(tolerance < overhang_width, "Tolerance must be smaller than the overhang width");
assert(divider_gap < cubby_size, "Divider gap must be smaller than the cubby size");

module box(grid_size_x, grid_size_y, cubby_size, cubby_height,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance, divider_gap) {

    // Derived Parameters
    internal_size_x = grid_size_x * cubby_size + (grid_size_x - 1) * divider_thickness;
    internal_size_y = grid_size_y * cubby_size + (grid_size_y - 1) * divider_thickness;
    outer_size_x = internal_size_x + 2 * wall_thickness;
    outer_size_y = internal_size_y + 2 * wall_thickness;
    box_height = cubby_height + lid_thickness + overhang_height;
    lid_size_x = internal_size_x + 2 * overhang_width;
    lid_size_y = internal_size_y + 2 * overhang_width;

    // Outer Box with Cubbies
    difference() {
        // Main box
        cube([outer_size_x, outer_size_y, box_height]);

        // Subtract cubbies
        for (x = [0:grid_size_x-1]) {
            for (y = [0:grid_size_y-1]) {
                translate([wall_thickness + x * (cubby_size + divider_thickness),
                           wall_thickness + y * (cubby_size + divider_thickness),
                           wall_thickness]) {
                    cube([cubby_size, cubby_size, box_height]);
                }
            }
        }

        // Subtract walls above cubbies
        translate([wall_thickness, wall_thickness, cubby_height])
        cube([internal_size_x, internal_size_y, box_height-cubby_height]);

        // Subtract lid (with tolerance)
        translate([wall_thickness-overhang_width, 0, cubby_height])
        lid(grid_size_x, grid_size_y, cubby_size,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance,
            notch_x, notch_y, notch_thickness, notch_gap);

        // Subtract spot above lid on near-side
        translate([wall_thickness, 0, cubby_height])
        cube([internal_size_x, wall_thickness, overhang_height+lid_thickness]);

        // Divider gaps
        if(divider_gap > 0) {
            divider_add = (cubby_size / 2) - (divider_gap / 2 );
            for (x = [0:grid_size_x-1]) {
                translate([wall_thickness + x * (cubby_size + divider_thickness) + divider_add,
                           wall_thickness,
                           wall_thickness]) {
                    cube([divider_gap, internal_size_y, box_height]);
                }
            }
            for (y = [0:grid_size_y-1]) {
                translate([wall_thickness,
                           wall_thickness + y * (cubby_size + divider_thickness) + divider_add,
                           wall_thickness]) {
                    cube([internal_size_x, divider_gap, box_height]);
                }
            }
        }
    }
}

module lid(grid_size_x, grid_size_y, cubby_size,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance,
            notch_x, notch_y, notch_thickness, notch_gap) {

    // Derived Parameters for the Lid
    internal_size_x = grid_size_x * cubby_size + (grid_size_x - 1) * divider_thickness;
    internal_size_y = grid_size_y * cubby_size + (grid_size_y - 1) * divider_thickness;
    lid_size_x = internal_size_x + 2 * overhang_width + 2*tolerance;
    lid_size_y = internal_size_y + overhang_width + wall_thickness + tolerance;

    // Lid with notch
    difference() {
        // Create lid
        cube([lid_size_x, lid_size_y, lid_thickness + 2*tolerance]);

        color("orange")
        translate([lid_size_x / 2, notch_gap, lid_thickness - notch_thickness / 2])
        cube([notch_x, notch_y, notch_thickness], center = true);

        color("red")
        translate([0, lid_size_y/2, lid_thickness + 2*tolerance])
        rotate([0, 45, 0])
        cube([overhang_width, lid_size_y, lid_thickness], center=true);

        color("green")
        translate([lid_size_x, lid_size_y/2, lid_thickness + 2*tolerance])
        rotate([0, -45, 0])
        cube([overhang_width, lid_size_y, lid_thickness], center=true);

        color("blue")
        translate([lid_size_x/2, lid_size_y, lid_thickness + 2*tolerance])
        rotate([45, 0, 0])
        cube([lid_size_x, overhang_width, lid_thickness], center=true);

    }
}

if (render_box) {
    // Create the Box
    box(grid_size_x, grid_size_y, cubby_size, cubby_height,
            divider_thickness, wall_thickness, lid_thickness,
            overhang_width, overhang_height, tolerance, divider_gap);
}

if (render_lid) {
    internal_size_y = grid_size_y * cubby_size + (grid_size_y - 1) * divider_thickness;
    outer_size_y = internal_size_y + 2 * wall_thickness;

    lid_trans = (render_box && render_lid) ?
        render_in_place ?
        animate ?
            [wall_thickness - overhang_width + tolerance,
                -outer_size_y * abs(cos(($t * 100) * PI)),
                cubby_height + tolerance] :
            [wall_thickness - overhang_width + tolerance, 0, cubby_height + tolerance] :
            [grid_size_x * cubby_size + 10 + wall_thickness * 2 + 3, 0, 0] :
            [0, 0, 0];

    translate(lid_trans)
    lid(grid_size_x, grid_size_y, cubby_size,
        divider_thickness, wall_thickness, lid_thickness,
        overhang_width, overhang_height, 0,
        notch_x, notch_y, notch_thickness, notch_gap);
}
```


[User]
I'm looking to create a parametric piano hinge in OpenSCAD, and I want it to be flexible enough to handle a few different use cases. I've seen some examples where you can either modify an existing object to have a hinge, or create a standalone hinge that can be attached to other parts. I need both.

Specifically, I need a module called `pianoHinge` that takes a child object and modifies it to have a hinge. This module should take arguments for:
  - `translation`: a 3D vector specifying where the hinge should be placed relative to the child object
  - `width`: the length of the hinge
  - `diameter`: the diameter of the hinge pin
  - `bridge`: a 2D vector representing the depth and height of the "bridges" that connect the hinge to the object. If no bridge is needed, setting this to `[0,0]` should omit them.
  - `mirrorBridge`: a boolean that determines if the bridge is mirrored or not.

The other module, `externalPianoHinge`, should generate just the hinge. It also needs:
  - `width`: length of the hinge.
  - `diameter`: diameter of the hinge pin.
  - `bridge`: a 2D vector for the bridge depth and height. `[0,0]` will omit the bridge.
  - `bridgeAngle`: an angle for the bridge to connect to other objects. I need this because sometimes I don't want to attach to a flat face. It should default to 180.

Both modules need to have customizable length and diameter. The hinge "core" (the cylindrical part with the gaps in it) should be built in segments, so when the width changes, the number of segments changes. The length of those segments is determined by the diameter of the hinge pin, and any extra width should be distributed evenly among the segments. Also, the bridge should be an optional piece. I need that for situations where the hinge needs to connect to a flat surface. The external hinge also needs that angle parameter, so I can easily attach it to something off of a flat surface.

I don't need any extra functionality beyond this. I want to avoid relying on any specific external libraries unless there's a good reason to.

[Assistant]
pianoHinge.scad:
```
/***********************************
Parametric Piano Hinge
pyrefyre

2024-09-07:
    externalPianoHinge has the option to angle connections to
    other objects so now you can print hinges off the printbed

Two functions to create hinges; each should produce the exact same object,
just different workflows:
 - pianoHinge modifies a singular child object that you want to put a hinge on
 - externalPianoHinge is just a hinge, good for joining two separately defined objects

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

$fn = 40;

width = 20;
diameter = 3; // .1
bridgeDepth = 1; // .1
bridgeHeight = diameter/2;
bridgeAngle = 270; // 45

module pianoHingeNubs(d,h) {
    unit = h/6;

	union() {
		cylinder(h=unit, r=d/2);
		translate([0,0,unit]) cylinder(h=unit, r1=d/2, r2=d/4);
		translate([0,0,4*unit]) cylinder(h=unit, r1=d/4, r2=d/2);
		translate([0,0,5*unit]) cylinder(h=unit, r=d/2);
	}
}

module pianoHingeCore(gap,d,h) {
    unit = h/6;

    difference() {
        //base
        translate([0,0,unit+gap]) cylinder(h=4*unit-2*gap, r=d/2);

        // cutout
        translate([0,0,unit+gap]) cylinder(h=unit, r1=d/2, r2=d/4);
        translate([0,0,4*unit-gap]) cylinder(h=unit, r1=d/4, r2=d/2);
    }
}

module pianoHingeBridge(bridge,gap,d,h,angle=180) {
    bridgeLength = bridge[0]+d/2;
    unit = h/6;

    difference() {
        union() {
            // core
            translate([unit+gap,bridgeLength,d/2])
            rotate([180-angle,0,0])
            translate([0,0,-d/2])
            cube([4*unit-2*gap,bridgeLength,bridge[1]]);
            // nubs
            translate([0,0,0])
            cube([unit,bridgeLength,bridge[1]]);
            translate([5*unit,0,0])
            cube([unit,bridgeLength,bridge[1]]);
        }

        // cutout for hinge
        translate(
            [0,d/2,d/2]
            +[0,bridge[0],0]
        )
        rotate([0,90,0])
        cylinder(h=width,d=d);
    }
}

module externalPianoHinge(width,diameter,bridge=[0,0],bridgeAngle=180) {
    gap = .4;

    segmentWidth = 2*diameter;
    hinges = round(width/segmentWidth) < 1 ? 1 : round(width/segmentWidth);
    widthScaling = 1 + (width - hinges*segmentWidth) / (hinges*segmentWidth);
    scaledWidth = segmentWidth*widthScaling;

    // add hinges
    for(i = [0:1:hinges-1]) {
        translate([i*scaledWidth,0,0])
        union() {
            if(bridge != [0,0]) pianoHingeBridge(bridge,gap,diameter,scaledWidth,bridgeAngle);

            translate([0,bridge[0]+diameter/2,diameter/2])
            rotate([0,90,0]) {
                pianoHingeNubs(diameter,scaledWidth);
                pianoHingeCore(gap,diameter,scaledWidth);
            }
        }
    }
}

module pianoHingeBridgeCuts(bridge,gap,d,h,mirrorBridge=false) {
    bridgeLength = bridge[0]+d/2;
    unit = h/6;

    coreTranslation = mirrorBridge ? [unit,0,0] : [unit,bridgeLength,0];
    leftNubTranslation = mirrorBridge ? [0,bridgeLength,0] : [0,0,0];
    rightNubTranslation = mirrorBridge ? [5*unit-gap,bridgeLength,0] : [5*unit-gap,0,0];

    union() {
        // core
        translate(coreTranslation)
        cube([4*unit,bridgeLength,bridge[1]]);
        // nubs
        translate(leftNubTranslation)
        cube([unit+gap,bridgeLength,bridge[1]]);
        translate(rightNubTranslation)
        cube([unit+gap+.01,bridgeLength,bridge[1]]); // +.01? shhhh don't ask
        //(fr tho a little wall was sometimes present in the gap between the hinge and the target object)

        // cutout for hinge
        translate(
            [0,d/2,d/2]
            +[0,bridge[0],0]
        )
        rotate([0,90,0])
        cylinder(h=h,d=d);
    }
}

module pianoHinge(translation,width,diameter,bridge=[0,0],mirrorBridge=false) {
    gap = .4;

    segmentWidth = 2*diameter;
    hinges = round(width/segmentWidth) < 1 ? 1 : round(width/segmentWidth);
    widthScaling = 1 + (width - hinges*segmentWidth) / (hinges*segmentWidth);
    scaledWidth = segmentWidth*widthScaling;

    if(bridge != [0,0]) {
        difference() {
            children();

            for(i = [0:1:hinges-1]) {
                translate(translation+[i*scaledWidth,0,0])
                pianoHingeBridgeCuts(bridge,gap,diameter,scaledWidth,mirrorBridge);
            }
        }
    }

    // add hinges
    for(i = [0:1:hinges-1]) {
        translate([i*scaledWidth,0,0])
        union() {

            translate(translation+[0,bridge[0]+diameter/2,diameter/2])
            rotate([0,90,0]) {
                pianoHingeNubs(diameter,scaledWidth);
                pianoHingeCore(gap,diameter,scaledWidth);
            }
        }
    }
}

module demo() {
    pianoHinge([0,8,0],width,diameter,[bridgeDepth,bridgeHeight],true)
    cube([width,15+2*bridgeDepth+diameter,bridgeHeight]);
}

module externalDemo() {
    cube([width,8,bridgeHeight]);

    translate([0,8,0])
    externalPianoHinge(width,diameter,[bridgeDepth,bridgeHeight],bridgeAngle);

    translate([0,8+2*bridgeDepth+diameter,0])
    cube([width,7,bridgeHeight]);
}

module externalRotatedDemo() {
    intersection() {
        union() {
            rotate([45,0,0])
            {
                cube([width,8,bridgeHeight]);

                translate([0,8,0])
                externalPianoHinge(width,diameter,[bridgeDepth,bridgeHeight],bridgeAngle);
            }

            translate([0,8*cos(45)+(diameter+bridgeDepth)/2*sin(45),8*sin(45)])
            rotate([-45,0,0])
            cube([width,8,bridgeHeight]);
        }

        translate([0,-bridgeHeight*cos(45),bridgeHeight*sin(45)])
        cube([width,(8+bridgeDepth)*2*sin(45)+diameter,(8+bridgeDepth)*sin(45)+diameter]);
    }
}

//demo();

//translate([width+10,0,0])
//externalDemo();

//translate([width+10,0,0])
externalRotatedDemo();
```


[User]
Hey, I'm looking to get a design for a customizable ID card holder, kinda like the one I saw on Thingiverse. I want to be able to specify a text acronym that will appear on the front and top, and also be used in the inserts. The main body of the holder should have two halves that are glued together with space for a card to slide in.

Here are the specific things I'd like to be able to control:

*   **Text Acronym:** A string to be used on the front of both halves and in the inserts. It should be able to handle any string, but it's intended for acronyms like "USMC", "ARMY", "SWAT", etc.
*   **Text Size:** The size of the text on the holder.
*   **Overall dimensions:** The length and width of the main body of the holder. It should accommodate a standard credit-card or ID size.

The main body should have these features:
*   Rounded corners (cylinder sections) at each of the four corners.
*   An inner cutout that goes all the way around that's a bit larger than a card, which will define the space where the card is inserted.
*   A slot cut into one side of the body where the card is inserted. This slot should be a thin rectangle cut vertically into the side to allow the card to slide in.
*   A small semi-circular cut out to aid in pushing the card out.
*   A raised bevel around the inner cutout.
*   Spherical or nearly spherical features added around the outside of the card for additional detailing.
*   The text acronym should be displayed vertically on the front of both halves.

The inserts will:
*   Also display the same acronym text.
*   Have a solid backing behind the text and fit inside the card cutout. They will be added to the model later.
*   Have a slightly different height than each other.

The two halves of the holder should be designed so they can be glued together.

I'd prefer it if the design could be parametric using OpenSCAD, so I could easily adjust those parameters I mentioned earlier. Can you generate the OpenSCAD code for that? I think I saw something about using `hull` to connect cylinders and create the curved edges, but don't use `rotate_extrude` here because that's an extra step, unless it's unavoidable. Please also make sure the code is using the `offset` and `linear_extrude` functions to create text, and that there's a way to change the font. The `Stardos Stencil` font looks good if you don't have a suggestion for a better one, but ideally, it could be modified.

I don't need any special libraries for this, but try to make the code as clean as possible with well-named modules and variables to help make it easy to understand and adjust. Thanks!

[Assistant]
id_card.scad:
```
txt="USMC";//[ARMY,NAVY,ROTC,USAF,USCG,USMC,RAF,RN,SOG,SWAT,ECU,USF,UNC]
txtsz=9.5;

//****//
bottom(txt,txtsz);
top(txt,txtsz);
inserts(txt,txtsz);

module bottom(txt,txtsz){
dx=80/2;
dy=48.5/2;
$fn=40;
fnt="Stardos Stencil:style=Bold";
difference(){
hull(){//body
for(x=[-dx-20,dx+4])
for(y=[-dy-4,dy+4])
translate([x,y,0])
difference(){
cylinder(r=3,h=4);
rotate_extrude()
translate([3,0])
circle(r=1.6,$fn=4);
}
}

hull(){//card cutout
for(x=[-dx,dx])
for(y=[-dy,dy])
translate([x,y,3])
cylinder(r=3.2,h=3);
}

hull(){//card slot
for(x=[-dx])
for(y=[-dy,dy])
translate([x,y,-0.1])
cylinder(r=3.2,h=5);

translate([-22,0,2.4])
cube([1,54.5,5],center=true);
}

hull(){//face cutout
for(x=[-dx,dx])
for(y=[-dy,dy])
translate([x,y,-0.1])
cylinder(r=1,h=3.2);
}

hull(){//inner bevel
for(x=[-dx,dx])
for(y=[-dy,dy])
translate([x,y,-0.1])
cylinder(r1=2.7,r2=1,h=1.7);
}

translate([-52.5,-21,-0.1])
cylinder(r=6,h=5);

translate([-52.5,-21,-0.1])
cylinder(r1=7.7,r2=6,h=1.7);

translate([-52.5,8,-0.1])
rotate([0,0,90])
mirror([1,0,0])
linear_extrude(height=4.2)
offset(r=0.15)
text(str(txt),size=txtsz,valign="center",halign="center",font=fnt);

translate([-52.5,8,3.6])
cube([12,39,4],center=true);

for(x=[-dx-20,dx+4])
for(y=[-dy-4,dy+4])
translate([x,y,4])
sphere(r=1.7);
}
}

module top(txt,txtsz){
dx=80/2;
dy=48.5/2;
$fn=40;
fnt="Stardos Stencil:style=Bold";
translate([0,67,0])
difference(){
union(){
hull(){
for(x=[-dx-20,dx+4])
for(y=[-dy-4,dy+4])
translate([x,y,0])
difference(){
cylinder(r=3,h=3);
rotate_extrude()
translate([3,0])
circle(r=1.6,$fn=4);
}
}
for(x=[-dx-20,dx+4])
for(y=[-dy-4,dy+4])
translate([x,y,3])
sphere(r=1.2);
}

hull(){
for(x=[-dx,dx])
for(y=[-dy,dy])
translate([x,y,-0.1])
cylinder(r1=2.7,r2=1,h=1.7);
}
hull(){
for(x=[-dx,dx])
for(y=[-dy,dy])
translate([x,y,-0.1])
cylinder(r=1,h=3.2);
}
translate([-52.50,21,-0.1])
cylinder(r=6,h=5);

translate([-52.50,21,-0.1])
cylinder(r1=7.7,r2=6,h=1.7);

translate([-52.5,-8,-0.1])
rotate([0,0,90])
mirror([1,0,0])
linear_extrude(height=4.2)
offset(r=0.15)
text(str(txt),size=txtsz,valign="center",halign="center",font=fnt);

translate([-52.5,-8,3.6])
cube([12,39,4],center=true);

}
}

module inserts(txt,txtsz){
$fn=50;
fnt="Stardos Stencil:style=Bold";
translate([0,-8,0])
union(){
linear_extrude(height=3)
offset(r=-0.14)
text(str(txt),size=txtsz,valign="center",halign="center",font=fnt);
translate([0,0,0.7])
cube([38.4,11.4,1.4],center=true);
}

translate([0,8,0])
union(){
linear_extrude(height=3.4)
offset(r=-0.14)
text(str(txt),size=txtsz,valign="center",halign="center",font=fnt);
translate([0,0,0.9])
cube([38.4,11.4,1.8],center=true);
}
}
```


[User]
Hey, I'm looking to create a Reversi (Othello) game set using OpenSCAD and need some help designing the files. I'd like to have a board and coins (or disks) that are parametric so I can easily adjust the size. I've got a basic idea but I need it all written from scratch.

Here's the general concept:

**Board:**

*   The board should be a square with a grid of 8x8 cells.
*   It will have a border all the way around the outside edge, and lines in between all the cells creating the grid.
*   The top of the board should have a raised section for each cell so the cell borders stand out, so there will be a lower level underneath each cell.
*   I'd like to be able to adjust the overall board width, height of the main board, and the height of the raised borders/gridlines, and the width of those borders/gridlines.
*   I would also like the board to be generated as its own object which I can enable/disable for generating the model.

**Coins:**

*   The coins should be cylindrical with a flat top and bottom.
*   I want to make a circular cutout (similar to what's generated by the rotate_extrude function) that's centered on the coin's height to allow for a dual-color effect, which will be done by pausing the printer at that point to swap the filament.
*   I'd like to be able to control the overall coin height, and the diameter of the coin. The diameter should be slightly smaller than the size of the cells on the board.
*   I also want to generate these as a separate object that I can toggle on/off.

**General Parameters:**

I'd need parameters for:

*   `boardWidth`: Total width of the square board in mm.
*   `boardHeight`: Height of the main board in mm.
*   `boardBorderHeight`: Height of the raised border/gridlines in mm.
*   `boardBorderWidth`: Width of the border/gridlines in mm.
*   `coinHeight`: Height of the coin/disk in mm.
*   `coinsD`: Diameter of the coin/disk in mm.
*   `board`: Boolean variable to toggle board generation.
*   `coins`: Boolean variable to toggle coin generation.

I will print with 0.2mm layer height and can add pauses in the printing to change the filament color for the coins.

Could you generate the necessary OpenSCAD files? I'm not using any external libraries, so this should all be in a single file. Thanks!

[Assistant]
Reversi.scad:
```

/*[Board:]*/
// in mm
boardWidth=206;
// in mm
boardHeight=2.4;
// in mm
boardBorderHeight=1.2;
// in mm
boardBorderWidth=1.2;
/*[Coins:]*/
// in mm
coinHeight=3;
/*[build:]*/
board=true;
coins=false;
/*[Hiden:]*/
$fn=200;

cell=(boardWidth-9*boardBorderWidth)/8;
coinsD=cell*0.9;
if (board){
    difference()
    {
        cube([boardWidth,boardWidth,boardHeight+boardBorderHeight]);
        for(y = [0 : 7]){
            for(x = [0 : 7]){
                color("red")
                    translate([boardBorderWidth+x*cell+x*boardBorderWidth,boardBorderWidth+y*cell+y*boardBorderWidth,boardHeight])
                        cube([cell,cell,boardHeight+boardBorderHeight]);
            }
        }
    }
}

if (coins){
    for(y = [0 : 7]){
        for(x = [0 : 7]){
            color("red")
                translate([cell/2+boardBorderWidth+x*cell+x*boardBorderWidth,cell/2+boardBorderWidth+y*cell+y*boardBorderWidth,boardHeight])
                    difference(){
                        cylinder(h=coinHeight, d=coinsD, center=false);
                            color("blue")
                                translate([0, 0, coinHeight/2])
                                rotate_extrude(convexity = 10)
                                    translate([coinsD/2, 0, 0])
                                        rotate([0,120,0])
                                        circle(d = 1, $fn = 3);
                    }
            }
        }
}

```


[User]
I'd like to create a parametric arc template for woodworking, using OpenSCAD. I need to be able to define the smallest and largest arc radius, as well as the increment between arcs. I want to be able to use either inches or centimeters for all dimensions, and have the conversion happen automatically.

The template should have a quarter-circle shape, with slots for a pencil to trace the arc. The pencil slots need to be tapered - wider at the top and narrower at the bottom, for better accuracy with different types of pencils. I also need to control the thickness of the template itself.

I want radial lines extending from the center of the arcs, and these should also be parametric; I want to define the angles for the radial lines and also their width. The template should also include text labels indicating the radius for each arc. The font, font size, and the padding around the text need to be configurable.

Finally, I'd like the option to add an edge lip to the template, either on the right or left side, that hangs over the edge of the wood. This should be configurable - no lip, right lip, or left lip. The height of the lip should also be configurable. The corners of the template should be rounded off.

I'm aiming to be able to generate this template with these customizations:
*   Native units (inches or centimeters)
*   Minimum arc radius
*   Maximum arc radius
*   Increment between arcs
*   Pencil slot width (top and bottom)
*   Template thickness
*   Radial line angles
*   Radial line width
*   Text font
*   Text padding
*   Text size adjustment
*   Edge lip type (none, right, or left)
*   Edge lip height
*   Corner rounding radius

I'm not looking for any specific libraries. Can you help me write the necessary OpenSCAD code?

[Assistant]
multi-arc-template.scad:
```

// 90 degree arc template
// rlb408 2024-08

// The center of the pencil slot is exactly where the line should be drawn.

//------------------------------- CUSTOMIZER PARAMETERS ---------------
// Some of these don't work in Thingiverse as of 2024-08 as Thingiverse is way out of date

/* [BASIC SHAPE Parameters] */

// Native units for the size and steps
native_units = "inches"; //[inches,centimeters]

// Smallest arc on the template, in native units
minimum_native = 0.5; //[0.5:0.05:200]

// Largest arc on the template, in native units, limited only by printer bed size
maximum_native = 4; //[0.5:.05:200]

// Increment for the arcs in native units.  Too small and there won't be room
step_native = 0.25; //[1:0.05:10]

// How far to extend the template on the sides to allow for the text, in mm. Make this bigger if the text doesn't fit
extension_xy = 15; //[10:25]

// Thickness, in mm
thickness = 2.4; //[1:0.1:4]

corner_rounding = 2;

// The pencil slot dimensions.  The slot is tapered, wider at the top.  Make these
//  numbers smaller if using a fine-tipped mechanical pencil.  Currently scaled for
//  standard woodworking pencil

/* [PENCIL SLOT Parameters] */

// The pencil slots are this wide on the top, in mm.
marker_width_top = 3.2; //[1:0.1:4]

// The pencil slots are this wide on the bottom, in mm
marker_width_bottom = 1.8;  //[1:0.1:4]

/* [LINE MARKS Parameters] */

// Width of the the radials lines
line_width = 1;

// Nominal line and text depth.  Will be adjusted to be thickness/2 if this is > thickness/2
nominal_line_depth = 0.6;

// Angle increments to put the radial lines
radials = [30,45];

/* [TEXT Parameters] */

// Font for the text
font = "Avenir:style=Black";

// Text padding, when near other things
text_padding = 2;

// Font size is calculated, but it's ballooned out by this much for legibility. 0.04 = 4%
font_expansion = 0.04;

/* [LIP Parameters] */

// What type of lip?  The lip hangs over the edge of the wood.  It's not practical for outside corners since every radius would need its own lip
lip_type = "none"; //[none,right,left]

// Edge lip depth if either is selected to be created
edge_lip_height = 12;

assert(minimum_native <= maximum_native,"Minimum must be <= maximum");

// dependent variables

metric = (native_units=="centimeters");
function mm(native)= (metric ? native*10 : native*25.4);

minimum = mm(minimum_native);
maximum = mm(maximum_native);
step = mm(step_native);

echo(minimum_native=minimum_native,maximum_native=maximum_native,step_native=step_native);
echo(minimum=minimum,maximum=maximum,step=step);
fontsize = min(extension_xy/4,step*0.8); //step*0.6;
echo(fontsize=fontsize);

// Depth of the radial lines and the text
line_depth = min(thickness/2,nominal_line_depth);

// How much bigger the outer portion is, where the radial labels exist
outer_extra = min(step*2,+marker_width_top/2+fontsize+text_padding*2);
echo(outer_extra=outer_extra);

/* [CURVE parameters] */
$fn = 90;
text_fn = 32;

module CustomixerEnd(){}

// Epsilon, to accommodate OpenSCAD slivvers
e = 0.05;
e2 = e*2;

// Flip the model over.  The results in better lettering legibility when printed in two colors
translate([0,0,thickness]) rotate([180,0,90])
template();

module template()
{
    difference() {
        union() {
            intersection() {
                cylinder(r=maximum+outer_extra,h=thickness);
                union() {
                    smooth_cube([maximum+outer_extra,maximum+outer_extra,thickness],r=corner_rounding);
                    cube([minimum,minimum,thickness]);
                }
            }

            // Create the extensions on the X then Y axes.
            // The extensions need to accomodate the size markers
            translate([0,-extension_xy,0])
            smooth_cube([maximum+step,extension_xy+thickness,thickness],r=corner_rounding);
            difference() {
                translate([-extension_xy,0,0])
                smooth_cube([extension_xy+thickness,maximum+step,thickness],r=corner_rounding);

                translate([-extension_xy/2,text_padding,thickness-line_depth])
                linear_extrude(line_depth+e) {
                    text(metric?"cm":"in.",font=font,size=extension_xy*0.33,valign="bottom",halign="center");
                }
            }

            // If wanted, add the edge lips

            if ( edge_lip_height > 0 ) {
                // X direction
                if ( lip_type == "right" ) {
                    translate([marker_width_bottom/2,-thickness,thickness])
                    rotate([-90,0,0])
                    smooth_cube([maximum+step-marker_width_bottom/2,edge_lip_height+thickness,thickness], r=corner_rounding);
                }
                // Y direction
                if ( lip_type == "left" ) {
                    translate([-thickness, marker_width_bottom/2,thickness])
                    rotate([0,90,0])
                    smooth_cube([edge_lip_height+thickness, maximum+step-marker_width_bottom/2,thickness], r=corner_rounding);
                }
            }
        }

        //SUBTRACT

        // The calculations are done in native units and only converted to mm for the models,
        // to avoid rounding problems with the labels
        extend_ends = marker_width_bottom;
        for ( r=[minimum_native:step_native:maximum_native] ) {
            translate([0,0,-e])
            arc(r=r, h=thickness+e2, top_width=marker_width_top, bottom_width=marker_width_bottom);
            // the end circles extend downward if there's an edge lip,  They're cones to
            // avoid the need for support material
            if ( edge_lip_height > 0 ) {
                translate([mm(r),0,-extend_ends])
                cylinder(d1=e, d2=marker_width_bottom,h=extend_ends);
                translate([0,mm(r),-extend_ends])
                cylinder(d1=e, d2=marker_width_bottom,h=extend_ends);
            }
            do_y_label(r);
            do_x_label(r);
        }

        // Place a pencil hole in the center
        cylinder(d1=marker_width_bottom, d2=marker_width_top,h=thickness);

        // Draw the radials
        for ( r=radials ) {
            for ( a=[0:r:90] ) {
                rotate([0,0,a])
                translate([minimum-marker_width_top/2-line_width,-line_width/2,thickness-line_depth])
                cube([maximum-minimum+marker_width_top+line_width*2,line_width,line_depth+e]);

                // Add label to the end of the radial
                if ( a%90 != 0 ) {
                    rotate([0,0,a])
                    translate([maximum+marker_width_top/2+text_padding,0,thickness-line_depth])
                    linear_extrude(line_depth+e)
                    rotate([0,0,-90])
                    offset(fontsize*font_expansion) // a little fatter to stand out
                    text(str(a),font=font, size=fontsize,halign="center",valign="bottom", $fn=text_fn);
                }
            }
        }
    }

}

// An extruded 2d arc, different widths at top and bottom

module arc(r, h, top_width, bottom_width)
{
    rotate_extrude(angle=90) // Won't work with Thingiverse customizer
    translate([mm(r),0])
    rotate([0,0,90])
    polygon([ [0,-bottom_width/2], [h,-top_width/2],
    [h, top_width/2], [0,bottom_width/2]
    ]);

    // Extend the arc a little with a rounded end
    translate([mm(r),0,0])
    cylinder(d1=bottom_width, d2=top_width,h=h);
    translate([0,mm(r),0])
    cylinder(d1=bottom_width, d2=top_width,h=h);
}

module do_y_label(r)
{
    translate([-text_padding,mm(r),thickness-line_depth])
    linear_extrude(line_depth+e)
    offset(fontsize*font_expansion) // a little fatter to stand out
    text(str(r),font=font,valign="center",size=fontsize,halign="right", $fn=text_fn);
}
module do_x_label(r)
{
    translate([mm(r),-text_padding,thickness-line_depth])
    rotate([0,0,180])
    linear_extrude(line_depth+e)
    rotate([0,0,90])
    offset(fontsize*font_expansion) // a little fatter to stand out
    text(str(r),font=font,valign="center",size=fontsize,halign="left", $fn=text_fn);
}

module rounded_square(v,r=3)
{
    offset(r) offset(-r) square(v);
}

module smooth_cube(v,r=3)
{
    linear_extrude(v[2])
    rounded_square([v[0],v[1]],r);
}

```


[User]
Okay, I need an OpenSCAD design for a highly customizable knob that can be used with both board-mounted trimmers and shafted potentiometers or rotary encoders. I've got some specific requirements and options I'd like to be able to adjust.

Here's the breakdown of the knob's design, from the bottom up:

**Stem:**
*   **Outer Stem (optional):**  A cylindrical sheath surrounding the inner stem. I need to control its outer diameter and height below the inner stem.  This part should be optional; I need to be able to hide it if needed.
*   **Inner Stem:** The base of the knob.  I need to specify its diameter and height. This is where the actuator connects.

**Middle:**
*   **Flange:**  A wider cylinder above the stem, meant to act as a stop against the case. I need to be able to set its diameter and height.
*   **Through-Case Cylinder:**  A cylindrical section that extends through the case. Again, diameter and height are needed.

**Knob:**
*   **Shape:**  The main manipulatable part. I want to control its diameter at the bottom and top, and its height. It can be either a frustum (sloping sides) or a cylinder depending on the knurling style used.
*   **Knurling:** I want three options for the knob's surface:
    *   **Smooth:** No knurling.
    *   **Grooved:** Vertical grooves around the knob.
        *   I need to control the number of grooves, their diameter, horizontal offset from the knob's surface, and the vertical offset from the bottom of the knob.
        *   I also need control over whether a groove is centered on the x-axis or if the x-axis runs between two grooves.
    *   **Hatched:** A cross-hatched pattern.
        *   I need to set the size and depth of the knurling, and the height of smooth sections at the top and bottom of the knurled area.

**Actuator:**
*   I need this to work for two types of components:
    *   **Board-Mounted Trimmers:**  These have a cross-shaped indentation. I need to independently control the length, width, and depth of both lines of the cross. The actuator should be rotated by a set number of degrees relative to the knob's pointer.
    *   **Stemmed Potentiometers/Encoders:** I need options for smooth shafts, knurled shafts, and "D" shafts (with a flat side)
        *   For a **smooth shaft** I need to set the diameter and depth.
        *   For a **knurled shaft** I need to set the number of knurls, their depth, and the depth of the shaft within the knob.
        *   For a **D shaft** I need to set the depth of the flat and its height from the bottom of the stem.
*   I also need to be able to rotate the actuator relative to the pointer.

**Pointer:**
*   I need a way to add a pointer to the top or side of the knob. It can be:
    *   **None:** No pointer.
    *   **Inset:** Grooves on the side and/or top of the knob.  I need to set the length, height, width and depth of the groove.
    *   **Outset:** A small rectilinear raised section on the side or top of the knob. I need to control its length, width, height, and vertical offset from the top of the knob.  I also need to set how far the top and side extend outward from the base surface.
    *    **Inset and Outset:**  Both the inset and outset pointer.
    *   **Dot:** A circular dot on the top surface, with controls for diameter, depth, and offset from the side of the knob.

I'd like this to be parametric, so I can adjust these dimensions and settings using variables. I also want to be able to easily use the generated knob module in other projects so, the main design should be encapsulated as a module. The code needs to use the `KnurledFinishLib_v2_1.scad` library when generating a hatched knurling surface.

I also want the code to output a console message at the start with the values of all the various parameters.

[Assistant]
TrimmerPotentiometerKnob.scad:
```
/*
Trimmer/Potentiometer knob
© 2024 Alexander Hajnal
v1.0

License: CC BY-NC-SA
Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
https://creativecommons.org/licenses/by-nc-sa/4.0/

Generates knobs for trimmers/potentiometers.

To disable any part set its height to 0.

If you want to use this module in your own code, note the parameters to
the module in the Console log and simply include the Knob module in your
code along with the "use <KnurledFinishLib_v2_1.scad>" line.  None of the
initialization code below is needed for that application.
*/

// This module is required for "Hatched" knurling of the knob.  It should
// be placed either in the OpenSCAD module directory or in the same
// directory as this script.
use <KnurledFinishLib_v2_1.scad>

/* [ General settings ] */

// Number of sides on a circle
$fn=64;

// Stem outer (d, h); see also "Hide stem outer" under "Actuator"
s_o = [ 11.6, 5.2 ]; // 0.1

// Stem inner (d, h)
s_i = [ 10.0, 5.0 ]; // 0.1

// Flange (d, h)
f = [ 15.0, 2.0 ]; // 0.1

// Through-case cylinder (d, h)
tc = [ 12.0, 2.0 ]; // 0.1

// Knob (lower d, upper d, h)
k = [ 12.0, 10.0, 15.0 ]; // 0.1

/* [ Actuator ] */

// Actuator style
a = "Trimmer"; // [ "Trimmer", "Stem (smooth)", "Stem (knurled)", "Stem (flattend/D)" ]

// Hide stem outer
hide_s_o = false;

// Rotate actuator relative to knob (degrees)
a_r = 0; // 1

// Trimmer actuator primary (l, w, h)
a_p = [ 3.8, 0.8, 2.0 ]; // 0.1

// Trimmer actuator secondary (l, w, h)
a_s = [ 3.8, 0.8, 1.2 ]; // 0.1

// Stem size (d, h)
a_stem = [ 6.0, 15 ]; // 0.1

// Stem flat (inset, h)
a_flat = [ 1.5, 12 ]; // 0.1

// Stem knurling (count, h, inset)
a_knurl = [ 18, 10, 0.5 ]; // 0.1

/* [ Knob knurling ] */

// Knob knurling style (Note; hatched knobs are always cylindrical)
kk_s = "Grooved"; // [ "None", "Grooved", "Hatched" ]

/* [ Knob knurling: Grooved ] */

// Knob grooved knurling number
kk_g_n = 12; // 1

// Knob grooved knurling diameter
kk_g_d = 2.0; // 0.1

// Knob grooved knurling horizontal offset
kk_g_oh = 1.25; // 0.05

// Knob grooved knurling vertical offset
kk_g_ov = 2.5; // 0.1

// Align knob knurling
kk_align = "Groove on axis"; // [ "Groove on axis", "Axis between grooves" ]

/* [ Knob knurling: Hatched ] */

// Knob hatched knurling size
kk_h_s = 2.0; // 0.1

// Knob hatched knurling depth
kk_h_d = 0.4; // 0.1

// Knob hatched knurling smooth section above
kk_h_sa = 0.5; // 0.1

// Knob hatched knurling smooth section below
kk_h_sb = 1.5; // 0.1

/* [ Pointer ] */

// Pointer style
p = "Outset"; // [ "None", "Inset", "Outset", "Inset and Outset", "Dot" ]

// Pointer outset dimensions ( l, w, h, v_offset )
p_o_d = [ 5.0, 1.2, 14.5, 0.0 ]; // 0.1

// Pointer outset extensions ( l, h )
p_o_e = [ 1.25, 0.75 ]; // 0.1

// Pointer inset distances ( l, h, w, d )
p_i = [ 5.0, 10.0, 1.2, 2.0 ]; // 0.1

// Pointer dot (d, h, offset)
p_d = [ 2.5, 1, 2 ]; // 0.1

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

kk_rotate = ( kk_align == "Groove on axis" )
          ? 0
          : // Axis between grooves
            (kk_g_n>0)
            ? 180/kk_g_n
            : 0;

// Generates a single knob
module Knob( a_p, a_s, a_r, a_stem, a_flat, a_knurl, s_i, s_o, hide_s_o, f, tc, k, kk_s, kk_g_n, kk_g_d, kk_g_oh, kk_g_ov, kk_h_s, kk_h_d, kk_h_sa, kk_h_sb, kk_rotate, p, p_o_d, p_o_e, p_i, p_d ) {
  echo( "Knob: " );
  echo( a_p=a_p );
  echo( a_s=a_s );
  echo( a_r=a_r );
  echo( a_stem=a_stem );
  echo( a_flat=a_flat );
  echo( a_knurl=a_knurl );
  echo( s_i=s_i );
  echo( s_o=s_o );
  echo( hide_s_o=hide_s_o );
  echo( f=f );
  echo( tc=tc );
  echo( k=k );
  echo( kk_s=kk_s );
  echo( kk_g_n=kk_g_n );
  echo( kk_g_d=kk_g_d );
  echo( kk_g_oh=kk_g_oh );
  echo( kk_g_ov=kk_g_ov );
  echo( kk_h_s=kk_h_s );
  echo( kk_h_d=kk_h_d );
  echo( kk_h_sa=kk_h_sa );
  echo( kk_h_sb=kk_h_sb );
  echo( kk_rotate=kk_rotate );
  echo( p=p );
  echo( p_o_d=p_o_d );
  echo( p_o_e=p_o_e );
  echo( p_i=p_i );
  echo( p_d=p_d );

  // Actuator (trimmer)
  if ( a == "Trimmer" ) {
    rotate( [ 0, 0, a_r ] ) {
      // Actuator primary
      if (a_p.z > 0)
        translate([0, 0, -a_p.z/2])
          cube(a_p, center=true);

      // Actuator secondary
      if (a_s.z > 0)
        rotate([0,0,90])
          translate([0, 0, -a_s.z/2])
            cube(a_s, center=true);
    }
  }

  difference() {
    union() {
      // Stem inner
      translate([0, 0, s_i.y/2])
        cylinder( h=s_i.y, d=s_i.x, center=true );

      // Stem outer
      if ( ! hide_s_o ) {
        if ( s_o.x>0 && s_o.y>0 ) {
          s_i_h = s_o.y+s_i.y;
          difference() {
            translate([0, 0, -(s_i_h)/2+s_i.y])
              cylinder( h=s_i_h, d=s_o.x, center=true );
            translate([0, 0, -(s_i_h)/2+s_i.y])
              cylinder( h=s_i_h+0.2, d=s_i.x, center=true );
          }
        }
      }

      // Flange
      if (f.y > 0)
        translate([0, 0, s_i.y + f.y/2])
          cylinder( h=f.y, d=f.x, center=true );

      // Through-case cylinder
      if (tc.y > 0)
        translate([0, 0, s_i.y + f.y + tc.y/2])
          cylinder( h=tc.y, d=tc.x, center=true );

      // Knob
      if (k.z > 0)
      translate([0, 0, s_i.y + f.y + tc.y])
      union() {
        difference() {

          // Knob
          union() {

            if ( ( kk_s == "None" ) || ( kk_s == "Grooved" ) ) {
              // Unadorned knob
              translate([0, 0, k.z/2])
                cylinder( h=k.z, d1=k.x, d2=k.y, center=true );
            }

            if ( kk_s == "Hatched" ) {
              // Cross-hatched knurling

              // Lower smooth section
              if (kk_h_sb > 0) {
                cylinder( h=kk_h_sb, d=k.x-2*kk_h_d );
              }

              // Knurling
              translate( [0,0,kk_h_sb] )
              knurl(	k_cyl_hg = k.z - kk_h_sa - kk_h_sb,
                      k_cyl_od = k.x - kk_h_d,
                      knurl_wd =  kk_h_s,
                      knurl_hg =  kk_h_s,
                      knurl_dp =  kk_h_d,
                      e_smooth =  1,
                      s_smooth =  0);

              // Upper smooth section
              if (kk_h_sa > 0) {
                translate( [0,0,k.z-kk_h_sa] )
                  cylinder( h=kk_h_sa, d=k.x-2*kk_h_d );
              }
            }
          } // END Knob

          // Vertical grooved knurling
          if ( kk_s == "Grooved" ) {
            rotate([0,0,kk_rotate]) {
              for ( n = [ 0 : 1: kk_g_n-1 ] ) {
                rotate( [ 0, 0, 360/kk_g_n*n ] )
                  render() difference() {
                    multmatrix( m = [ [ 1, 0, -(k.x-k.y)/(2*k.z), 0 ],
                                      [ 0, 1, 0, 0 ],
                                      [ 0, 0, 1, 0 ],
                                      [ 0, 0, 0, 1 ] ] )
                      translate([k.x/2+kk_g_oh-kk_g_d/2, 0, k.z/2 + 0.5])
                          cylinder( h=k.z+1, d=kk_g_d, center=true );
                    translate([0, 0, kk_g_ov/2-0.5])
                      cylinder( h=kk_g_ov+1, d=k.x+2*(kk_g_d+kk_g_oh), center=true );
                  }
              }
            }
          }

          if (
               ( p == "Inset" ) ||
               ( p == "Inset and Outset" )
             ) {
            // Grooves
            l = p_i[0];
            h = p_i[1];
            w = p_i[2];
            d = p_i[3];
            h_upper = (h < k.z) ? h : k.z;
            h_lower = (h > k.z) ? k.z - h : 0;

            if ( kk_s == "Grooved" ) {
              // Side groove
              multmatrix( m = [ [ 1, 0, -(k.x-k.y)/(2*k.z), 0 ],
                                [ 0, 1, 0, 0 ],
                                [ 0, 0, 1, 0 ],
                                [ 0, 0, 0, 1 ] ] )
                translate([k.x/2 - d/2 + 0.5, 0, k.z - h_upper/2 + 0.5 ])
                  cube([d+1, w, h_upper+1], center=true );

              // Top groove
              multmatrix( m = [ [ 1, 0, -(k.x-k.y)/(2*k.z), 0 ],
                                [ 0, 1, 0, 0 ],
                                [ 0, 0, 1, 0 ],
                                [ 0, 0, 0, 1 ] ] )
                translate([k.x/2 - l/2 + 0.5, 0, k.z - d/2 + 0.5 ])
                    cube([l+1, w, d+1], center=true );
            } else {
              // Side groove
              translate([k.x/2 - d/2 + 0.5, 0, k.z - h_upper/2 + 0.5 ])
                cube([d+1, w, h_upper+1], center=true );

              // Top groove
              translate([k.x/2 - l/2 + 0.5, 0, k.z - d/2 + 0.5 ])
                cube([l+1, w, d+1], center=true );
            }
          }
          if (
               ( p == "Dot" )
             ) {
            // Dot on top surface
            d      = p_d[0];
            h      = p_d[1];
            offset = p_d[2];

            kd = ( kk_s == "Hatched" ) ? k.x : k.y;
            translate([kd/2 - offset, 0, k.z - h/2 + 0.5] )
              cylinder( h=h+1, d=d, center=true );
          }
        }

        if ( ( p == "Outset" ) || ( p == "Inset and Outset" ) ) {
          // Outset pointer
          l        = p_o_d[0];
          w        = p_o_d[1];
          h        = p_o_d[2];
          v_offset = p_o_d[3];
          l_extend = p_o_e[0];
          h_extend = p_o_e[1];
          translate([k.x/2-l/2+l_extend/2, 0, k.z - h/2 + h_extend/2 - v_offset])
            cube( [ l+l_extend, w, h+h_extend ], center=true );
        }
      }
    }

    // Hole in bottom for stemmed potentiometers

    if ( a == "Stem (smooth)" ) {
      // Smooth shaft
      translate( [0, 0, a_stem.y/2-0.5] )
       cylinder(d=a_stem.x, h=a_stem.y+1, center=true);
    }

    if ( a == "Stem (knurled)" ) {
      // Knurled shaft
      count = a_knurl[0];
      h     = a_knurl[1];
      inset = a_knurl[2];
      d     = a_stem.x;
      sh    = a_stem.y;
      //union() {
      difference() {
        translate( [0, 0, sh/2-0.5] )
         cylinder(d=d, h=sh+1, center=true);
        translate( [0, 0, sh-h] )
          for ( n = [ 0 : 1: count-1 ] ) {
            rotate( [ 0, 0, 360/count*n ] )
              linear_extrude(h+1)
                polygon( [ [0,d/2-inset], [inset,d/2], [-inset,d/2] ] );
        }
      }
    }

    if ( a == "Stem (flattend/D)" ) {
      // Smooth shaft with flattened cutout section
      inset = a_flat[0];
      h     = a_flat[1];
      d     = a_stem.x;
      sh    = a_stem.y;
      //union() {
      difference() {
        translate( [0, 0, sh/2-0.5] )
         cylinder(d=d, h=sh+1, center=true);
        translate( [d/2-inset/2+0.5, 0, h/2+(sh-h+0.5)] )
          cube( [inset+1, d, h+1], center=true );
      }
    }
  }
}

Knob(
      a_p, a_s,  // Actuator (trimmer, primary and secondary)
      a_r,       // Actuator rotation relative to knob (degrees)
      // Stemmed actuator:
      a_stem, a_flat, a_knurl,
      s_i, s_o,  // Stem (inner and outer)
      hide_s_o,  // Set true to hide outer stem
      f,         // Flange
      tc,        // Through-case cylinder
      k,         // Knob
      kk_s,      // Knob knurling style
      // Knob grooved knurling:
      kk_g_n, kk_g_d, kk_g_oh, kk_g_ov,
      // Knob hatched knurling:
      kk_h_s, kk_h_d, kk_h_sa, kk_h_sb,
      // Pointer:
      kk_rotate, p, p_o_d, p_o_e, p_i, p_d
    );

```
KnurledFinishLib_v2_1.scad:
```
/*
 * knurledFinishLib_v2_1.scad
 *
 * Written by aubenc @ Thingiverse
 *
 * This script is licensed under the Public Domain license.
 *
 * http://www.thingiverse.com/thing:31122
 *
 * Derived from knurledFinishLib.scad (also Public Domain license) available at
 *
 * http://www.thingiverse.com/thing:9095
 *
 * Usage:
 *
 *	 Drop this script somewhere where OpenSCAD can find it (your current project's
 *	 working directory/folder or your OpenSCAD libraries directory/folder).
 *
 *	 Add the line:
 *
 *		use <knurledFinishLib_v2_1.scad>
 *
 *	 in your OpenSCAD script and call either...
 *
 *    knurled_cyl( Knurled cylinder height,
 *                 Knurled cylinder outer diameter,
 *                 Knurl polyhedron width,
 *                 Knurl polyhedron height,
 *                 Knurl polyhedron depth,
 *                 Cylinder ends smoothed height,
 *                 Knurled surface smoothing amount );
 *
 *  ...or...
 *
 *    knurl();
 *
 *	If you use knurled_cyl() module, you need to specify the values for all and
 *
 *  Call the module ' help(); ' for a little bit more of detail
 *  and/or take a look to the PDF available at http://www.thingiverse.com/thing:9095
 *  for a in depth descrition of the knurl properties.
 */

module knurl(	k_cyl_hg	= 12,
					k_cyl_od	= 25,
					knurl_wd =  3,
					knurl_hg =  4,
					knurl_dp =  1.5,
					e_smooth =  2,
					s_smooth =  0)
{
    knurled_cyl(k_cyl_hg, k_cyl_od,
                knurl_wd, knurl_hg, knurl_dp,
                e_smooth, s_smooth);
}

module knurled_cyl(chg, cod, cwd, csh, cdp, fsh, smt)
{
    cord=(cod+cdp+cdp*smt/100)/2;
    cird=cord-cdp;
    cfn=round(2*cird*PI/cwd);
    clf=360/cfn;
    crn=ceil(chg/csh);

    echo("knurled cylinder max diameter: ", 2*cord);
    echo("knurled cylinder min diameter: ", 2*cird);

	 if( fsh < 0 )
    {
        union()
        {
            shape(fsh, cird+cdp*smt/100, cord, cfn*4, chg);

            translate([0,0,-(crn*csh-chg)/2])
              knurled_finish(cord, cird, clf, csh, cfn, crn);
        }
    }
    else if ( fsh == 0 )
    {
        intersection()
        {
            cylinder(h=chg, r=cord-cdp*smt/100, $fn=2*cfn, center=false);

            translate([0,0,-(crn*csh-chg)/2])
              knurled_finish(cord, cird, clf, csh, cfn, crn);
        }
    }
    else
    {
        intersection()
        {
            shape(fsh, cird, cord-cdp*smt/100, cfn*4, chg);

            translate([0,0,-(crn*csh-chg)/2])
              knurled_finish(cord, cird, clf, csh, cfn, crn);
        }
    }
}

module shape(hsh, ird, ord, fn4, hg)
{
	x0= 0;	x1 = hsh > 0 ? ird : ord;		x2 = hsh > 0 ? ord : ird;
	y0=-0.1;	y1=0;	y2=abs(hsh);	y3=hg-abs(hsh);	y4=hg;	y5=hg+0.1;

	if ( hsh >= 0 )
	{
		rotate_extrude(convexity=10, $fn=fn4)
		polygon(points=[	[x0,y1],[x1,y1],[x2,y2],[x2,y3],[x1,y4],[x0,y4]	],
					paths=[	[0,1,2,3,4,5]	]);
	}
	else
	{
		rotate_extrude(convexity=10, $fn=fn4)
		polygon(points=[	[x0,y0],[x1,y0],[x1,y1],[x2,y2],
								[x2,y3],[x1,y4],[x1,y5],[x0,y5]	],
					paths=[	[0,1,2,3,4,5,6,7]	]);
	}
}

module knurled_finish(ord, ird, lf, sh, fn, rn)
{
    for(j=[0:rn-1])
    let(h0=sh*j, h1=sh*(j+1/2), h2=sh*(j+1))
    {
        for(i=[0:fn-1])
        let(lf0=lf*i, lf1=lf*(i+1/2), lf2=lf*(i+1))
        {
            polyhedron(
                points=[
                     [ 0,0,h0],
                     [ ord*cos(lf0), ord*sin(lf0), h0],
                     [ ird*cos(lf1), ird*sin(lf1), h0],
                     [ ord*cos(lf2), ord*sin(lf2), h0],

                     [ ird*cos(lf0), ird*sin(lf0), h1],
                     [ ord*cos(lf1), ord*sin(lf1), h1],
                     [ ird*cos(lf2), ird*sin(lf2), h1],

                     [ 0,0,h2],
                     [ ord*cos(lf0), ord*sin(lf0), h2],
                     [ ird*cos(lf1), ird*sin(lf1), h2],
                     [ ord*cos(lf2), ord*sin(lf2), h2]
                    ],
                faces=[
                     [0,1,2],[2,3,0],
                     [1,0,4],[4,0,7],[7,8,4],
                     [8,7,9],[10,9,7],
                     [10,7,6],[6,7,0],[3,6,0],
                     [2,1,4],[3,2,6],[10,6,9],[8,9,4],
                     [4,5,2],[2,5,6],[6,5,9],[9,5,4]
                    ],
                convexity=5);
         }
    }
}

module knurl_help()
{
	echo();
	echo("    Knurled Surface Library  v2  ");
   echo("");
	echo("      Modules:    ");
	echo("");
	echo("        knurled_cyl(parameters... );    -    Requires a value for each an every expected parameter (see bellow)    ");
	echo("");
	echo("        knurl();    -    Call to the previous module with a set of default parameters,    ");
	echo("                                  values may be changed by adding 'parameter_name=value'        i.e.     knurl(s_smooth=40);    ");
	echo("");
	echo("      Parameters, all of them in mm but the last one.    ");
	echo("");
	echo("        k_cyl_hg       -   [ 12   ]  ,,  Height for the knurled cylinder    ");
	echo("        k_cyl_od      -   [ 25   ]  ,,  Cylinder's Outer Diameter before applying the knurled surfacefinishing.    ");
	echo("        knurl_wd     -   [   3   ]  ,,  Knurl's Width.    ");
	echo("        knurl_hg      -   [   4   ]  ,,  Knurl's Height.    ");
	echo("        knurl_dp     -   [  1.5 ]  ,,  Knurl's Depth.    ");
	echo("        e_smooth   -    [  2   ]  ,,  Bevel's Height at the bottom and the top of the cylinder    ");
	echo("        s_smooth   -    [  0   ]  ,,  Knurl's Surface Smoothing :  File donwn the top of the knurl this value, i.e. 40 will snooth it a 40%.    ");
	echo("");
}

```


[User]
I'd like to design a fidget spinner with a textured surface, sort of like an old hand spinner I printed a while back. I want it to have a central cylinder with six arms coming off of it, though only three of those arms will have the texture. I'd like to be able to control the inner and outer diameters of the main cylinder, the outer diameter of the arms, and the size of the texture.

Specifically, the central cylinder should have an inner diameter, which I'll call `k_cyl_id`, and the outer diameter of the cylinder should be `k_cyl_id` plus some additional amount (`k_cyl_od`, which is defaulted to `k_cyl_id + 4`). The main cylinder's height will be `k_cyl_hg`. Six arms should extend out from the central cylinder, and each should be `k_3cyl_od` in diameter, but only three are textured. These textured arms will use a knurled surface effect as defined in `knurledFinishLib.scad`. The untextured arms should just be round cylinders. There should be a center join between the central cylinder and all six arms, controlled by a variable, `center_Join_Thikness`.

The knurled surface should have its width, height and depth controlled by variables `knurl_wd`, `knurl_hg`, and `knurl_dp` respectively. There's also a smoothing variable for the knurled surface, `s_smooth`, which can be set as a percentage, and smoothing at the cylinder ends, `e_smooth`. The texturing arms are also cylinder in shape. I want to also smooth the edges of all the cylinders, not just the knurled cylinders, with an approach like the rounded cylinder function which already exists in the provided code.

I'd like all of these values to be configurable via variables at the top of the file. The default dimensions I'm working with is `k_cyl_id` of 21, the main cylinder height as `k_cyl_hg` at 6.8. The `k_3cyl_od` should be defaulted to `k_cyl_id + 7`. The knurl width should be 3, the height should be 4, and the depth should be 1.5, and both `e_smooth` should be 2, with `s_smooth` default to 0. And center join thickness as 1.

I’ll be using the `knurledFinishLib.scad` library from Thingiverse, so you should account for that.

[Assistant]
figet.scad:
```
include <knurledFinishLib.scad>

/* [Rings] */
//  height
k_cyl_hg=6.8;
//  ring inner diameter
k_cyl_id=21;
// ring outer diameter
k_3cyl_od=k_cyl_id+7;
// 3 out ring outer diameter
center_Join_Thikness=1;
// center join thikness

/* [Surface] */
// Knurl polyhedron width
knurl_wd=3;
// Knurl polyhedron height
knurl_hg=4;
// Knurl polyhedron depth
knurl_dp=1.5;

e_smooth=2;      // Cylinder ends smoothed height
s_smooth=0;      // [ 0% - 100% ] Knurled surface smoothing amount
/* [advanced] */
k_cyl_od=k_cyl_id+4;
$fn = 100;

module roudedCylinder(cylH,cylD)
{
    difference(){
        color("red")
        cylinder(h=cylH,d=cylD+cylH);
        color("blue")
        translate([0, 0, cylH/2])
        rotate_extrude(convexity = 10)
        translate([(cylD+cylH)/2+cylH, 0, 0])
        circle(d = cylH*3);
    }
}

difference(){
    union(){
        difference(){
        cylinder(h=k_cyl_hg,d=2*k_cyl_id);
        for ( i = [1:1:6])
            rotate([0,0,i*(360/6)])
                translate([k_cyl_od+center_Join_Thikness,0,0])
                    knurled_cyl(k_cyl_hg, k_3cyl_od,knurl_wd, knurl_hg,knurl_dp,e_smooth, s_smooth);
        }
        for ( i = [1:1:3])
            rotate([0,0,2*i*(360/6)])
                translate([k_cyl_od,0,0])
                    knurled_cyl(k_cyl_hg, k_3cyl_od,knurl_wd, knurl_hg,knurl_dp,e_smooth, s_smooth);

        }
    roudedCylinder(k_cyl_hg,k_cyl_id);
        for ( i = [1:1:3])
            rotate([0,0,2*i*(360/6)])
                translate([k_cyl_od,0,0])
                    roudedCylinder(k_cyl_hg,k_cyl_id);
}
```


[User]
I'd like to design a parametric shelf for displaying figures, and I'd like to use OpenSCAD. I need it to be configurable in a few ways, and I'm thinking the overall structure will be a platform with supports underneath.

First, the platform should have configurable size (length, width, and thickness), and I need to be able to choose whether the corners are sharp or rounded with a configurable radius. I'd like to also have the option to have the back of the platform be flat or curved when it's against the wall.

Then, for supports, I want to choose between a curved style and a triangular style. I need to be able to configure the size of the supports and have an option to make them hollow or solid. I will also need to configure how many supports to generate, as well as the offset from the edges of the platform to the supports. The supports should be evenly spaced.

Finally, I want to have cutouts on the back of the platform and supports for adhesive strips. I need to specify the thickness of the adhesive strips. These cutouts should only be on the back faces, not any other faces. I want to be able to turn these on or off independently.

I think that should be all the functionality I need. I don't have a preference for specific libraries, so use whichever you think is best.

[Assistant]
shelf.scad:
```
/***********************************
Parametric Shelf
pyrefyre
***********************************/
/* [Curve Resolution] */
$fn = 60; // [60:128]

/* [Platform] */
platformSize = [120,120,10];
// set to 0 for sharp corners
platformCornerRadius = 5; // .1
// for sharp corners at wall
curveWallCorners = 0; // [1]

/* [Supports] */
supportStyle = 0; // [0:curved,1:triangle]
// 0 for filled, no hollow recommended for curved support
supportHollow = 0; // 1
supportNumber = 1;
// if putting adhesive strips on supports, add +2 to y and z recommended
supportSize = [60,10,100];
supportOffset = 10;

/* [Adhesive Strip Cutouts] */
adhesiveStripThickness = .5; // .1

// for adhesive strips on back of platform
usePlatformCutout = 1; // [1]
platformCutoutDepth = curveWallCorners ?
    platformSize[1]-2*platformCornerRadius
    : platformSize[1]-2*1;
platformCutoutSize = [
    adhesiveStripThickness,
    platformCutoutDepth,
    platformSize[2]-2
];

// for adhesive strips on back of supports
useSupportCutout = 1; // [1]
supportCutoutSize = [
    adhesiveStripThickness,
    supportSize[1]-2,
    supportSize[0]
];

module ogeeCurve(dimensions=[1,1]) {
    side = 10;
    radius = side/2;

    resize(dimensions)
    intersection() {
        union() {
            // left
            difference() {
                square(side/2);

                translate([0,radius])
                circle(radius);
            }

            // right
            translate([2*radius,radius])
            circle(radius);

            translate([radius,0])
            square(radius);
        }

        square(side);
    }
}

module semicircle(r) {
    intersection() {
        translate([0,r])
        square(2*r,true);
        circle(r);
    }
}

module roundedCube(dimensions=[1,1,1],radius=1) {
    if(radius) {
        x = dimensions[0]/2 - radius < radius ? 0 : dimensions[0]/2 - radius;
        y = dimensions[1]/2 - radius < radius ? 0 : dimensions[1]/2 - radius;

        translate([x + radius,y + radius,0])
        linear_extrude(dimensions[2])
        hull() {
            // top right
            translate([x,y,0])
            circle(radius);
            // bottom right
            translate([x,-1*y,0])
            circle(radius);
            // bottom left
            translate([-1*x,-1*y,0])
            circle(radius);
            // top right
            translate([-1*x,y,0])
            circle(radius);
        }
    }
    else
        cube(dimensions);
}

module supportProfile(dimensions=[1,1]) {
    resize(dimensions)
    union() {
        translate([0,.1])
        ogeeCurve([1,.9]);
        square([1,.1]);
    }
}

module curveSupport(size,hollowThickness=0) {
    translate([0,size[1],0])
    rotate([90,0,0])
    linear_extrude(size[1])
    translate([0,size[0]])
    rotate(-90)
    difference() {
        supportProfile([size[0],size[2]]);
        if(hollowThickness) {
            offset(r=-hollowThickness)
            supportProfile([size[0],size[2]]);
        }
    }
}

module rightTriangle(legA,legB) {
    points = [
        [0,0],
        [0,legA],
        [legB,0]
    ];
    polygon(points);
}

module triangularSupport(size,hollowThickness=0) {
    translate([0,size[1],0])
    rotate([90,0,0])
    linear_extrude(size[1])
    difference() {
        rightTriangle(size[0],size[2]);
        if(hollowThickness) {
            offset(r=-hollowThickness)
            rightTriangle(size[0],size[2]);
        }
    }
}

module platformCutout(enable,cutoutOffset,platformCutout) {
    if(enable) {
        difference() {
            children();

            // platform command strip slot
            translate([0,cutoutOffset,1])
            cube(platformCutout);
        }
    }
    else
        children();
}

module supportCutout(enable,supportCutout) {
    if(enable) {
        difference() {
            children();

            // platform command strip slot
            translate([0,1,-1])
            cube(supportCutout);
        }
    }
    else
        children();
}

module shelf(platformSize,platformCornerRadius,curveWallCorners,usePlatformCutout,supportStyle,supportHollow,useSupportCutout,supportNumber,supportSize,supportOffset) {
    intersection() {
        union() {
            // supports
            supportSpacing = (platformSize[1]-2*supportOffset-supportSize[1])/(supportNumber-1);
            for(i = [0:1:supportNumber-1]) {
                translate(supportNumber == 1 ?
                    [
                        0,
                        (platformSize[1]-supportSize[1])/2,
                        platformSize[2]
                    ]
                :
                    [
                        0,
                        supportOffset+i*supportSpacing,
                        platformSize[2]
                    ]
                )

                supportCutout(useSupportCutout,supportCutoutSize)
                if(supportStyle == 0)
                    curveSupport(supportSize,supportHollow);
                else if (supportStyle == 1)
                    triangularSupport(supportSize,supportHollow);
            }

            // platform
            new_platformSize = curveWallCorners ? platformSize
            :
                [
                    platformSize[0]+platformCornerRadius,
                    platformSize[1],
                    platformSize[2]
                ];
            platformCutoutOffset = curveWallCorners ?
                platformCornerRadius
                : 1;

            platformCutout(usePlatformCutout,platformCutoutOffset,platformCutoutSize)
            translate(curveWallCorners ? [0,0,0] : [-platformCornerRadius,0,0])
            roundedCube(new_platformSize,platformCornerRadius);
        }

    // cuts off back of shelf for square corners against wall
    cube([platformSize[0],platformSize[1],platformSize[2]+supportSize[2]]);
    }
}

shelf(platformSize,platformCornerRadius,curveWallCorners,usePlatformCutout,supportStyle,supportHollow,useSupportCutout,supportNumber,supportSize,supportOffset);
```


[User]
Hey there! I'm looking to get a custom marker and wipes holder designed in OpenSCAD, and I think I've got a good idea of what I'm after.

Basically, I need a holder that has two main sections: one for markers and one for a cylindrical container of wipes. The marker section should be customizable in terms of how many markers it can hold and how those markers are arranged in rows. The wipe section should be a simple cylinder. I'd also like to be able to customize the overall size, wall thickness, and hole sizes.

Here's a breakdown of the specific parameters I'd like to be able to adjust:

**General Holder Dimensions:**

*   `markers`:  The total number of marker holes (e.g. 8). I'd like to be able to go up to at least 32.
*   `rows`: The number of rows for the marker holes (e.g., 2). I expect this to be 1-5.
*   `height`: The overall height of the holder in mm. (e.g., 75). I need this to be between 50 and 200 mm.
*   `wall_thickness`: The thickness of the walls of the holder in mm. (e.g., 3). I think 2 to 5 should be plenty.

**Wipe Section:**

*   `wipe_rad`: The radius of the wipe container holder in mm. (e.g., 42).  The range should be from 10 to 100 mm

**Marker Section:**

*   `marker_rad`: The radius of each marker hole in mm. (e.g., 9). Something between 3 and 15 mm should do the trick.
*   `marker_ridge_height`: The height of the small ridge inside each marker hole to keep the markers from falling out (e.g., 5). Make sure I can adjust this between 2 and 8 mm.
*    `marker_spacing`: Spacing between each marker hole.  I should be able to change this from 0 up to 5.

**Advanced Parameters:**

* `precision`: A parameter to adjust the rendering precision for the circles. Let's say a value from 50 to 200 should be good.
* `nudge`:  A small value to help prevent some rendering issues. Let's call it 0.1 and I don't need to be able to adjust it.

The marker holes should be arranged in a grid pattern, and if there's an odd number of rows, the holes should be offset on every other row. I need a ridge on each marker hole at the top to hold the markers.

I'd like to be able to export as an STL for 3D printing and I will probably be printing this at 15-20% infill. I'm not worried about supports. Also, I'd really like for the design to be fully parametric so I can change these values and re-render as needed.

I think that covers everything! Let me know if you have any questions or need more clarification. I am excited to see what you come up with.

[Assistant]
marker-and-wipes.scad:
```
// Parameters
/* [Holder Dimensions] */
// Number of marker holes
markers = 8; // [1:32]
// Number of rows for marker holes
rows = 2; // [1:5]
// Height of the holder (mm)
height = 75; // [50:200]
// Thickness of walls (mm)
wall_thickness = 3; // [2:5]

/* [Wipe Section] */
// Radius of the wipe holder (mm)
wipe_rad = 42; // [10:100]

/* [Marker Section] */
// Radius of marker holes (mm)
marker_rad = 9; // [3:15]
// Height of the ridge in marker holes (mm)
marker_ridge_height = 5; // [2:8]
// Spacing between markers (mm)
marker_spacing = 2; // [0:5]

/* [Advanced] */
// Rendering precision (higher is smoother but slower)
precision = 100; // [50:200]
// Small offset to prevent rendering issues
nudge = 0.1;

// Useful reusable functions

// The size of the marker hole plus gaps
marker_size = function() marker_rad * 2 + marker_spacing;

// The distance from y=0 to draw the marker block
marker_block_y_offset = function() -(wipe_rad + wall_thickness);

// Distance from x=0 to start drawing marker blocks
marker_hole_x_offset = function() wipe_rad + wall_thickness + marker_size()/2;
// Distance from y=0 to start drawing marker blocks
marker_hole_y_offset = function() marker_block_y_offset() + marker_size()/2 + wall_thickness;

// Additional offset for every odd marker
odd_offset = function() marker_rad + marker_spacing;

/**
 * Creates the main holder structure with wipes and marker sections
 */
module main_holder() {
    difference() {
        union() {
            // Wipe holder cylinder
            cylinder(h = height + wall_thickness, r = wipe_rad + wall_thickness, $fn = precision);

            // Markers block
            translate([0, marker_block_y_offset(), 0])
            cube([
                marker_hole_x_offset() + odd_offset() + ceil(markers / rows)*marker_size() + wall_thickness,
                rows*marker_size() + 2*wall_thickness,
                height + wall_thickness
            ]);
        }

        // Wipe hole
        translate([0, 0, wall_thickness])
        cylinder(h = height + wall_thickness, r = wipe_rad, $fn = precision);

        // Marker holes
        translate([
            marker_hole_x_offset(),
            marker_hole_y_offset(),
            wall_thickness])
        marker_holes();
    }
}

/**
 * Creates alternating pattern of marker holes
 */
module marker_holes() {
    cols = ceil(markers / rows);
    for (row = [0:rows-1]) {
        for (col = [0:cols-1]) {
            if (row * cols + col < markers) {
                x = col * (marker_size() + wall_thickness) + (row % 2)*odd_offset();
                y = row * marker_size();
                translate([x, y, 0])
                marker_hole();
            }
        }
    }
}

/**
 * Creates a single marker hole with ridge
 */
module marker_hole() {
    union() {
        // Main hole
        cylinder(r = marker_rad, h = height, $fn = precision);

        // Ridge at the top
        translate([0, 0, height - marker_ridge_height])
        cylinder(
            r1 = marker_rad,
            r2 = marker_rad + (wall_thickness / 2) - nudge,
            h = marker_ridge_height,
            $fn = precision
        );
    }
}

// Generate the holder
main_holder();

```


[User]
I'm looking to create a parametric filament swatch using OpenSCAD, and I want it to be easily customizable. The basic idea is a rectangular swatch with a channel running around the perimeter. I'd like to be able to customize the overall dimensions, the wall thickness, the depth of the channel, and the diameter of the mounting holes.

Specifically, the swatch should be a rectangle with rounded corners, and there should be a channel running around the inside of the rectangle, also with rounded corners. There will be four cylindrical holes, one in each corner, for mounting.

Additionally, I want to include two sets of test squares, one on the left that acts as a series of rectangular holes, and the other on the right, that are extruded squares rising off of the main surface. The holes on the left should gradually fill in, starting at the bottom, so that the lower-most hole is the most shallow and the highest one is fully open. Finally, the swatch needs to be able to have text printed into a channel on it, so I can add information like the brand, color, material and printing temperature.

Here's a more detailed breakdown of the parameters I'd like to control:

*   **Overall Dimensions:**
    *   `x1`: Width of the outer rectangle.
    *   `y1`: Height of the outer rectangle.
*   **Wall Thickness:**
    *   `d1`: Thickness of the outer wall and channel wall.
*   **Channel Depth:**
    *   `w1`: The depth of the channel that goes around the inside of the rectangle.
*   **Hole Diameter:**
    *   `z1`: Diameter of the mounting holes.
*   **Test Square Dimensions:**
    *   `cd`: the extrusion height of the raised test squares
*   **Margins**
    *   `margin`: Margin between elements
    *   `Zmargin`: Z margin for filling the test squares
*   **Filleting:**
    *   I'd like to use a fillet to round the outer edges of the main rectangle, and I'd like to use similar methods for the inner edges too.
    *   I'd like to be able to control the size of the fillets
*   **Text:**
    *  The four lines of text should be within the channel on one of the sides.

For the test squares on the left side, I'm thinking of having a series of holes that fill up gradually from the bottom. That is the lowest hole will be filled at the bottom up to the Zmargin and the higher ones will fill more until the highest hole will be a fully open hole. On the other side I want solid squares raised by `cd`.

I'd like to use some custom modules to make the fillets and rounding easier, if that makes sense. I'm not sure exactly how to implement them, but I'm thinking modules like `fillet2d` and `rounding2d` to handle 2D filleting and rounding, and similar modules to apply the fillets on the top and bottom.

Can you generate the appropriate OpenSCAD code? I'm not looking to use any specific libraries, so anything you generate should be self-contained.

[Assistant]
Proto2.scad:
```
// Settings
Brand = "Brand";
Color = "Color";
Material = "Material";
Print_Temp = "240C";
// Dimensions
$fn = 24;
x1 = 64;
y1 = 42;
// Hole diameter
z1 = 2.56;
// Wall thickness
d1 = 3;
// Channel depth
w1 = 3;
// Test squares
cd = 0.64;

margin = 3;
Zmargin = 1.5;

// Modules for filleting and rounding
module fillet2d(r) {
    offset(r = -r) offset(delta = r) children(0);
}

module rounding2d(r) {
    offset(r = r) offset(delta = -r) children(0);
}

function filletDepth(r, d, i) = r * cos(asin(d * i / r));

module topBottomFillet(b = 0, t = 2, r = 1, s = 4, e = 1) {
    if (e == 1) {
        topFilletPeice(t = t, r = r, s = s) children(0);
        bottomFilletPeice(b = b, r = r, s = s) children(0);

        render()
        difference() {
            children(0);

            translate([0, 0, t - r])
            linear_extrude(r + 1)
            offset(delta = 1e5)
            projection()
            children(0);

            translate([0, 0, b - 1])
            linear_extrude(r + 1)
            offset(delta = 1e5)
            projection()
            children(0);

        }
    }
    if (e == 0) children(0);
}

module topFillet(t = 2, r = 1, s = 4, e = 1) {
    if (e == 1) {
        topFilletPeice(t = t, r = r, s = s) children(0);

        render()
        difference() {
            children(0);
            translate([0, 0, t-r])
            linear_extrude(r + 1)
            offset(delta = 1e5)
            projection()
            children(0);
        }
    }
    if (e == 0) children(0);
}

module bottomFillet(b = 0, r = 1, s = 4, e = 1) {
    if (e == 1) {
        bottomFilletPeice(b = b, r = r, s = s) children(0);

        render()
        difference() {
            children(0);
            translate([0, 0, b - r - 1])
            linear_extrude(r + 1)
            offset(delta = 1e5)
            projection()
            children(0);
        }
    }
    if (e == 0) children(0);
}

module topFilletPeice(t = 2, r = 1, s = 4) {
    d = r/s;

    for (i = [0:s]) {
        x = filletDepth(r, d, i);
        z = d * (s - i + 1);
        translate([0, 0, t - z])
        linear_extrude(d)
        offset(delta = -r + x)
        projection(true)
        translate([0, 0, -t + z])
        children(0);
    }
}

module bottomFilletPeice(b = 0, r =1, s = 4) {
    d = r/s;

    for (i = [0:s]) {
        x = filletDepth(r, d, i);
        z = d * (s - i);
        translate([0, 0, b + z])
        linear_extrude(d)
        offset(delta = -r + x)
        projection(true)
        translate([0, 0, b - z])
        children(0);
    }
}

module LeftTestSquares() {
    // Calculate dimensions and positions
    Right = 4;
    inner_width = x1 - w1 - d1 * 2;
    inner_height = y1 - w1 - d1 * 2;
    available_height = inner_height - 2 * margin;
    rect_height = (available_height - (Right - 1) * margin) / Right;
    rect_width = min(1.5 * rect_height, inner_width - 2 * margin);

    // Create rectangular holes along the right side
    difference(){
        linear_extrude(z1){
            for (i = [0:Right-1]) {
                translate([
                    inner_width / 2 - rect_width / 2 - margin,
                    -inner_height / 2 + margin + i * (rect_height + margin) + rect_height / 2,
                    0
                ]) {
                    rounding2d(d1/2) {
                        square([rect_width, rect_height], center=true);
                    }
                }
            }
        }
        for (i = [0:Right-1]) {
            fill_percentage = (i + 1) / Right; // Percentage to fill up from the bottom
            fill_height = fill_percentage * (z1 - 2 * Zmargin); // Calculate fill height

            translate([
                inner_width / 2 - rect_width / 2 - margin,
                -inner_height / 2 + margin + i * (rect_height + margin) + rect_height / 2,
                Zmargin + fill_height // Offset by Zmargin and fill height
            ]) {
                linear_extrude(z1 - Zmargin * 2) {
                    rounding2d(d1/2) {
                        square([rect_width, rect_height], center=true);
                    }
                }
            }
        }
    }
}

module RightElevatedTestSquares() {
    // Calculate dimensions and positions
    Left = 4;
    inner_width = x1 - w1 - d1 * 2;
    inner_height = y1 - w1 - d1 * 2;
    available_height = inner_height - 2 * margin;
    rect_height = (available_height - (Left - 1) * margin) / Left;
    rect_width = min(1.5 * rect_height, inner_width - 2 * margin);

    // Create solid rectangles along the left side
    for (i = [0:Left-1]) {
        translate([
            -inner_width / 2 + rect_width / 2 + margin,
            -inner_height / 2 + margin + i * (rect_height + margin) + rect_height / 2,
            0
        ]) {
            linear_extrude(cd) { // Extrude by cd millimeters
                rounding2d(d1/2){
                    square([rect_width, rect_height], center=true);
            }
            }
        }
    }
}

module BCMT(){
    Left = 4;
    inner_width = x1 - w1 - d1 * 2;
    inner_height = y1 - w1 - d1 * 2;
    available_height = inner_height - 2 * margin;
    rect_height = (available_height - (Left - 1) * margin) / Left;
    rect_width = min(1.5 * rect_height, inner_width - 2 * margin);

    translate([0,0,-cd])
    linear_extrude(cd)
    union(){
        translate([-x1/4,(-inner_height / 2 + margin +
    3 * (rect_height + margin) + rect_height / 2)-1.5,0]){text(
    Brand, size=2.5);}
        translate([-x1/4,(-inner_height / 2 + margin +
    2 * (rect_height + margin) + rect_height / 2)-1.5,0]){text(
    Color, size=2.5);}
        translate([-x1/4,(-inner_height / 2 + margin +
    1 * (rect_height + margin) + rect_height / 2)-1.5,0]){text(
    Material, size=2.5);}
        translate([-x1/4,(-inner_height / 2 + margin +
    0 * (rect_height + margin) + rect_height / 2)-1.5,0]){text(
    Print_Temp, size=2.5);}
    }
}

// Main structure
difference(){
rotate([180,0,0])difference() {
    union() {
        difference() {
            linear_extrude(z1) {  // Big Square
                rounding2d(d1/2+w1/2) {
                    square([x1, y1], center=true);
                }
            }
            linear_extrude(cd) {  // Channel
                translate([0, 0, z1-cd])
                rounding2d(d1/2) {
                    square([x1-w1, y1-w1], center=true);
                }
            }
            union() {  // Cylindrical holes
                for (i = [0:3]) {
                    translate([
                        (x1/2-w1/2-d1/1.5) * pow(-1, i),
                        (y1/2-w1/2-d1/1.5) * pow(-1, floor(i/2)),
                        (z1*1.5)/3
                    ])
                    cylinder(h=z1*1.5, d=d1, center=true);
                }
            }
        }
        union() {  // Inner Square
            linear_extrude(z1) {
                rounding2d(d1/2) {
                    square([x1-w1-d1*2, y1-w1-d1*2], center=true);
                }
            }
        }
    }

    LeftTestSquares();
    // Add rectangles as holes
}

BCMT();
}RightElevatedTestSquares();
```


[User]
Hey, I'm looking for an OpenSCAD design for a monitor-mounted ring light holder, similar to the webcam mount I've been using (I've attached the .scad file for reference).  The key here is I need the ring light to be held securely, but I also want the design to be parametric so I can adjust it to different ring light sizes, different monitor thicknesses, and different screw sizes.

My ring light has a standard 1/4"-20 threaded hole in the base that I want to use for mounting. Also, I would love for the hinge mechanism to use the same sort of friction-fit design as the webcam mount I provided. It's a great way to make it adjustable. I'm picturing the design will have a top piece and bottom piece that clamps onto the monitor and is held together with a hinge. The top part would have a screw hole to mount the light. The top and bottom should have a similar rounded look to the webcam mount.

For libraries, please try to incorporate the BOLTS library to generate the 1/4-20 screw thread (though just the hole/clearance is fine) and use the same sort of parametric approach to the friction hinge as the provided webcam mount. Let me know if you have any questions. Thanks in advance for your help!

[Assistant]
webcam_mount_all-in-one.scad:
```
/*
  Parametric Webcam Mount
  pyrefyre
*/

$fn = 40;

width = 45;
depth = 50;
height = 15;
// thickness of the portion which hangs over monitor edge
overbite = 2; // .1

/* [Hinge] */
// recommended to bump the diameter by about .2mm bigger than your screw diameter
clearanceHole = 10.2; // .1

/* [Camera Mounting Screw] */
screwDiameter = 6; // .1
screwCutoutThickness = 1; // .1
fingerCutoutDiameter= depth > width ? width-2 : depth-2;

module rotate_about_axis(axis_position, rotation_angles) {
    // axis_position: [x, y, z] coordinates of the axis to rotate around
    // rotation_angles: [rx, ry, rz] angles in degrees to rotate around each axis

    translate([axis_position[0], axis_position[1], axis_position[2]]) // Translate back to the original position
    rotate(rotation_angles) // Apply rotation
    translate([-axis_position[0], -axis_position[1], -axis_position[2]]) // Translate to the axis
    children();
}

module silo2d(diameter,length) {
	radius = diameter/2;
	//assert(length >= radius, "Length must be >= radius!");
	intersection() {
		union() {
			translate([radius,length-radius])
			circle(radius);
			square([2*radius,length-radius]);
		}

		square([2*radius,length]);
	}
}

module frictionHinge2d(outerDiameter,innerDiameter,orig_length=0) {
	assert(outerDiameter > innerDiameter, "Outer diameter must be greater than inner diameter!");

	length = orig_length < outerDiameter ?  outerDiameter : orig_length;
	outerRadius = outerDiameter/2;
	innerRadius = innerDiameter/2;

	difference() {
		difference() {
				silo2d(outerDiameter,length);
				translate([outerRadius,length-outerRadius])
				circle(innerRadius);
		}

		difference() {
			translate([outerRadius,0])
			square([outerRadius,length]);
			translate([outerRadius,length-outerRadius])
			circle(outerRadius);
		}
	}
}

module roundedSquare(dimensions=[1,1],radius=1) {
    x = dimensions[0]/2 - radius < radius ? 0 : dimensions[0]/2 - radius;
    y = dimensions[1]/2 - radius < radius ? 0 : dimensions[1]/2 - radius;

    translate([x + radius,y + radius,0])
    hull() {
        // top right
        translate([x,y,0])
        circle(radius);
        // bottom right
        translate([x,-1*y,0])
        circle(radius);
        // bottom left
        translate([-1*x,-1*y,0])
        circle(radius);
        // top right
        translate([-1*x,y,0])
        circle(radius);
    }
}

module bottom() {
    rotate([0,90,0])
    linear_extrude(width)
    translate([-height/2,0])
    intersection() {
        roundedSquare([height,depth+height/2-overbite],height/2);
        square([height/2,depth-overbite]);
    }

    // hinge
    translate([width,depth-overbite,0])
    rotate([0,-90,0])
    linear_extrude(width/2)
    frictionHinge2d(height,clearanceHole);
}

module top() {
    // body
    difference() {
        cube([width,depth,height]);

        // overbite
        translate([0,overbite,0])
        cube([width,depth-overbite,height/2]);

        // finger cutout
        translate([width/2,depth/2,0])
        cylinder(h=height-screwCutoutThickness,d=fingerCutoutDiameter);

        // screw cutout
        translate([width/2,depth/2])
        cylinder(h=height,d=screwDiameter);
    }

    // hinge
    translate([width/2,depth,height])
    rotate([0,-90,0])
    mirror([1,0,0])
    linear_extrude(width/2)
    frictionHinge2d(height,clearanceHole);
}

translate(
    $preview
    ? [0,height,0]
    : [width+10,-depth+height,0]
)
rotate_about_axis([0,depth,height/2],[180,0,0])
top();
translate($preview ? [0,overbite,0] : [0,0,0]) bottom();
```