[User]
Hey, I'm looking for an OpenSCAD design for Minecraft tools, kind of like the existing one I found, but I want it to be a bit more flexible and also to include a few more tools. I'm thinking something that lets me easily change the size of the cubes that make up the tools and also lets me pick which tool to generate from a list. Also, the one I found added a little hang loop, which was great, so I'd want to make sure that's still an option.

Specifically, I'd need the following tools: a spade, a pickaxe, an axe, a hoe, a sword, an egg, a bow, and a bow with an arrow, just like that original example. I'd want to be able to set the cube size as a parameter. I would also need the option to add a hanger hole.

For libraries, I'm not sure what would be best here, but I don't think I need any MCAD or BOSL. I think that BOSL2 would be great if it could help. I'm fine if it ends up using things like dotSCAD or NopSCADlib as well. I'd like to have separate modules for each of the tools so that they can be easily added to and modified later. And I'd like everything to be parametric, so I can change the parameters and have it all regenerate quickly. I'd like that each tool is a module.

Also, one thing I'd really like to be able to do is to control the depth of the tool--that is, the z-dimension of each cube. The example I found has that hard-coded to one, and it would be really useful to set that as a parameter.

Could you generate the OpenSCAD files for that, please?

[Assistant]
MCTools.scad:
```
//Minecraft Tools
//Developed by Rich Pereira
//rich@pereira.com
//--------------------------
//More to come!
//--------------------------

//CUSTOMIZER VARIABLES

//	Select tool
tool = 8;	//	[1:Spade, 2:Pickaxe, 3:Axe, 4:Hoe, 5:Sword, 6:Egg, 7:Bow, 8: BowAndArrow]

//	Scale (tenths of mm per cube)
cube_size = 30;	//	[1:50]

//  Whether to add a hanger hole ath -1, -1
hanghole = 1;  // [0:No, 1: Yes]

//CUSTOMIZER VARIABLES END

scale([cube_size/10,cube_size/10,cube_size/10]) {
    if (tool==1) spade();
    if (tool==2) pick();
    if (tool==3) axe();
    if (tool==4) hoe();
    if (tool==5) sword();
    if (tool==6) egg();
    if (tool==7) bow();
    if (tool==8) bow_and_arrow();

    if (hanghole == 1) {
        translate([-2,0,0]) cube([3,1,1]);
        translate([-2,-2,0]) cube([3,1,1]);
        translate([-2,-1,0]) cube([1,1,1]);
        translate([0,-1,0]) cube([1,1,1]);
    }
}

module bow_and_arrow() {
    // move the model so the hanger hole is in a good place
    translate([16,14,0]) mirror([1,1,0]) {
    // body
    translate([10,14,0]) cube([4,1,1]);
    translate([7,13,0]) cube([8,1,1]);
    translate([5,12,0]) cube([9,1,1]);
    translate([4,11,0]) cube([6,1,1]);
    translate([3,10,0]) cube([4,1,1]);
    translate([2,9,0]) cube([3,1,1]);
    translate([2,8,0]) cube([3,1,1]);
    translate([1,7,0]) cube([3,1,1]);
    translate([1,6,0]) cube([3,1,1]);
    translate([1,5,0]) cube([3,1,1]);
    translate([0,4,0]) cube([3,1,1]);
    translate([0,3,0]) cube([3,1,1]);
    translate([0,2,0]) cube([3,1,1]);
    translate([0,1,0]) cube([3,1,1]);
    translate([1,0,0]) cube([1,1,1]);
    // string
    stringcube=1.1;
    translate([3,1,0]) cube([stringcube,stringcube,1]);
    translate([4,2,0]) cube([2*stringcube,stringcube,1]);
    translate([6,3,0]) cube([stringcube,stringcube,1]);
    translate([7,4,0]) cube([2*stringcube,stringcube,1]);
    translate([9,5,0]) cube([stringcube,stringcube,1]);
    translate([10,6,0]) cube([stringcube,2*stringcube,1]);
    translate([11,8,0]) cube([stringcube,stringcube,1]);
    translate([12,9,0]) cube([stringcube,2*stringcube,1]);
    translate([13,11,0]) cube([stringcube,stringcube,1]);

    // arrow
    translate([8,6,0]) cube([2,1,1]);
    translate([7,7,0]) cube([2,1,1]);
    translate([6,8,0]) cube([2,1,1]);
    translate([5,9,0]) cube([2,1,1]);
    translate([4,10,0]) cube([2,1,1]);
    translate([3,11,0]) cube([2,1,1]);
    translate([2,12,0]) cube([2,1,1]);
    translate([1,13,0]) cube([2,1,1]);
    translate([0,14,0]) cube([2,1,1]);
}
};

module bow() {
    // body
    translate([10,14,0]) cube([4,1,1]);
    translate([7,13,0]) cube([8,1,1]);
    translate([5,12,0]) cube([9,1,1]);
    translate([4,11,0]) cube([6,1,1]);
    translate([3,10,0]) cube([4,1,1]);
    translate([2,9,0]) cube([3,1,1]);
    translate([2,8,0]) cube([3,1,1]);
    translate([1,7,0]) cube([3,1,1]);
    translate([1,6,0]) cube([3,1,1]);
    translate([1,5,0]) cube([3,1,1]);
    translate([0,4,0]) cube([3,1,1]);
    translate([0,3,0]) cube([3,1,1]);
    translate([0,2,0]) cube([3,1,1]);
    translate([0,1,0]) cube([3,1,1]);
    translate([1,0,0]) cube([1,1,1]);
    // string
    stringcube=1.05;
    translate([3,2,0]) cube([stringcube,stringcube,1]);
    translate([4,3,0]) cube([stringcube,stringcube,1]);
    translate([5,4,0]) cube([stringcube,stringcube,1]);
    translate([6,5,0]) cube([stringcube,stringcube,1]);
    translate([7,6,0]) cube([stringcube,stringcube,1]);
    translate([8,7,0]) cube([stringcube,stringcube,1]);
    translate([9,8,0]) cube([stringcube,stringcube,1]);
    translate([10,9,0]) cube([stringcube,stringcube,1]);
    translate([11,10,0]) cube([stringcube,stringcube,1]);
    translate([12,11,0]) cube([stringcube,stringcube,1]);
};

module spade() {
    translate([9,12,0]) cube([3,1,1]);
    translate([8,11,0]) cube([5,1,1]);
    translate([7,10,0]) cube([6,1,1]);
    translate([6,9,0]) cube([7,1,1]);
    translate([7,8,0]) cube([5,1,1]);
    translate([6,7,0]) cube([5,1,1]);
    translate([5,6,0]) cube([3,1,1]);
    translate([4,5,0]) cube([3,1,1]);
    translate([3,4,0]) cube([3,1,1]);
    translate([2,3,0]) cube([3,1,1]);
    translate([0,2,0]) cube([4,1,1]);
    translate([0,1,0]) cube([3,1,1]);
    translate([1,0,0]) cube([2,1,1]);
    translate([9,6,0]) cube([1,1,1]);
};

module pick() {
    translate([4,12,0]) cube([5,1,1]);
    translate([3,11,0]) cube([9,1,1]);
    translate([4,10,0]) cube([8,1,1]);
    translate([8,9,0]) cube([4,1,1]);
    translate([7,8,0]) cube([6,1,1]);
    translate([6,7,0]) cube([3,1,1]);
    translate([5,6,0]) cube([3,1,1]);
    translate([4,5,0]) cube([3,1,1]);
    translate([3,4,0]) cube([3,1,1]);
    translate([2,3,0]) cube([3,1,1]);
    translate([1,2,0]) cube([3,1,1]);
    translate([0,1,0]) cube([3,1,1]);
    translate([0,0,0]) cube([2,1,1]);
    translate([10,7,0]) cube([3,1,1]);
    translate([10,6,0]) cube([3,1,1]);
    translate([10,5,0]) cube([3,1,1]);
    translate([10,4,0]) cube([3,1,1]);
    translate([11,3,0]) cube([1,1,1]);
};

module axe() {
    translate([8,14,0]) cube([2,1,1]);
    translate([7,13,0]) cube([4,1,1]);
    translate([6,12,0]) cube([5,1,1]);
    translate([5,11,0]) cube([7,1,1]);
    translate([5,10,0]) cube([7,1,1]);
    translate([6,9,0]) cube([7,1,1]);
    translate([7,8,0]) cube([6,1,1]);
    translate([6,7,0]) cube([3,1,1]);
    translate([5,6,0]) cube([3,1,1]);
    translate([4,5,0]) cube([3,1,1]);
    translate([3,4,0]) cube([3,1,1]);
    translate([2,3,0]) cube([3,1,1]);
    translate([1,2,0]) cube([3,1,1]);
    translate([0,1,0]) cube([3,1,1]);
    translate([0,0,0]) cube([2,1,1]);
    translate([10,7,0]) cube([2,1,1]);
};

module hoe() {
    translate([5,13,0]) cube([3,1,1]);
    translate([4,12,0]) cube([5,1,1]);
    translate([5,11,0]) cube([7,1,1]);
    translate([7,10,0]) cube([5,1,1]);
    translate([8,9,0]) cube([4,1,1]);
    translate([7,8,0]) cube([4,1,1]);
    translate([6,7,0]) cube([3,1,1]);
    translate([5,6,0]) cube([3,1,1]);
    translate([4,5,0]) cube([3,1,1]);
    translate([3,4,0]) cube([3,1,1]);
    translate([2,3,0]) cube([3,1,1]);
    translate([1,2,0]) cube([3,1,1]);
    translate([0,1,0]) cube([3,1,1]);
    translate([0,0,0]) cube([2,1,1]);
};

module egg() {
    translate([4,12,0]) cube([4,1,1]);
    translate([3,11,0]) cube([6,1,1]);
    translate([2,10,0]) cube([8,1,1]);
    translate([1,9,0]) cube([9,1,1]);
    translate([1,8,0]) cube([10,1,1]);
    translate([0,7,0]) cube([11,1,1]);
    translate([0,6,0]) cube([12,1,1]);
    translate([0,5,0]) cube([12,1,1]);
    translate([0,4,0]) cube([12,1,1]);
    translate([1,3,0]) cube([10,1,1]);
    translate([1,2,0]) cube([10,1,1]);
    translate([2,1,0]) cube([8,1,1]);
    translate([3,0,0]) cube([6,1,1]);
};

module sword() {
    translate([13,15,0]) cube ([3,1,1]);
    translate([12,14,0]) cube ([4,1,1]);
    translate([11,13,0]) cube ([5,1,1]);
    translate([10,12,0]) cube ([5,1,1]);
    translate([9,11,0]) cube ([5,1,1]);
    translate([8,10,0]) cube ([5,1,1]);
    translate([2,9,0]) cube ([2,1,1]);
    translate([2,8,0]) cube ([3,1,1]);
    translate([3,7,0]) cube ([7,1,1]);
    translate([3,6,0]) cube ([6,1,1]);
    translate([4,5,0]) cube ([4,1,1]);
    translate([3,4,0]) cube ([6,1,1]);
    translate([2,3,0]) cube ([3,1,1]);
    translate([0,2,0]) cube ([4,1,1]);
    translate([0,1,0]) cube ([3,1,1]);
    translate([0,0,0]) cube ([3,1,1]);
    translate([7,9,0]) cube ([5,1,1]);
    translate([6,8,0]) cube ([5,1,1]);
    translate([6,3,0]) cube ([4,1,1]);
    translate([8,2,0]) cube ([2,1,1]);
};

```


[User]
Hey, I need to design a solder tip holder and I'm thinking a customizable parametric design in OpenSCAD would be perfect. I have some ideas of what I'm looking for, but I need help coding it.

Basically, I want a box-like structure with cylindrical holes to hold the solder tips. I'd like to be able to specify the following parameters:

*   `tipGap`: The space between each tip hole, in millimeters.
*   `tipSize`: The diameter of the tip holes, in millimeters.
*   `tipsPerRow`: The number of tip holes in each row.
*   `tipsPerColumn`: The number of tip holes in each column.
*   `height`: The overall height of the box, in millimeters.
*   `baseHeight`: The height of the base of the box, in millimeters. The remaining height should be the depth of the tip holes.
*   I'd like the main structure to be a rounded cube, so you'll need to implement a `roundedCube` module, taking `xdim`, `ydim`, `zdim`, and `rdim` as parameters.

The tip holes should be created using a `drawTipHolders` module. This module should use a nested for loop to create cylinders using the tipSize parameter. The tip holes should be evenly spaced, accounting for tipGap and tipSize, using `tipGap+tipDim*2` for spacing, where `tipDim = tipSize/2`.

The main structure needs to use a `difference()` operation to create the holes. I'm thinking the module should translate the `roundedCube()` by `[length/2-(tipGap+tipDim), width/2-(tipGap+tipDim), -3]` where length and width are defined using `tipGap`, `tipSize`, `tipsPerRow` and `tipsPerColumn`.

I don't think I need any external libraries like BOSL, just the basic OpenSCAD functionality.

Can you help with this?

[Assistant]
solder_tip_holder.scad:
```
/*
Solder Tip Holder (c) by Jorge Monteiro

Solder Tip Holder is licensed under a
Creative Commons Attribution-ShareAlike 4.0 International.

You should have received a copy of the license along with this
work.  If not, see <http://creativecommons.org/licenses/by-sa/4.0/>.
*/

/* [Basics] */
// Space between tips in mm
tipGap = 3;
// Diameter of tip in mm
tipSize = 9;
// Number of tips you want to store per row
tipsPerRow = 3;
// Number of tips you want to store per column
tipsPerColumn = 3;
// Height of the tip box in mm
height = 18;
// Base of the box in mm
baseHeight = 3;

/* [Ignore] */
$fn=40*1;
tipDim = tipSize/2;
width = tipsPerRow * (tipGap + tipSize) + tipGap;
length = tipsPerColumn * (tipGap + tipSize) + tipGap;
tipHeight = height - baseHeight;

difference() {

    translate([length/2-(tipGap+tipDim), width/2-(tipGap+tipDim), -3]) roundedCube(length, width, height, 5);
    drawTipHolders();
};

module drawTipHolders() {
     for(row = [0 : 1 : tipsPerRow-1]) {
         for(column = [0 : 1 : tipsPerColumn-1]) {
            translate([(tipGap+tipDim*2)*column, (tipGap+tipDim*2)*row]) cylinder(tipHeight, tipDim, tipDim);
         }
     }

}

module roundedCube(xdim, ydim, zdim, rdim) {
    translate([-xdim/2,-ydim/2,0]){
        hull(){
            translate([rdim,rdim,0]) cylinder(r=rdim, h=zdim);
            translate([xdim-rdim,rdim,0]) cylinder(r=rdim, h=zdim);

            translate([rdim,ydim-rdim,0]) cylinder(r=rdim, h=zdim);
            translate([xdim-rdim,ydim-rdim,0]) cylinder(r=rdim, h=zdim);

        };
    };
};

```


[User]
Hi! I'm looking to design some landing feet for a small drone, specifically for a quadcopter frame. I'd like them to be parametric so I can easily adjust for different screw and nut sizes.

Essentially, the foot should have a cylindrical base that's 10mm tall and 5mm radius, with a sphere merged onto the bottom of the cylinder, also with a 5mm radius. There should be a hole going through the center of the cylindrical base for a screw. I need a hex shaped cavity for a nut, with a corresponding rectangular channel so you can insert the nut horizontally. The depth of this cavity/channel should match the nut height.

I'd like to be able to switch between M2.5 and M3 screws and nuts by setting a parameter. Let's call this `useM3nut`. If `useM3nut` is set to 0, it should default to M2.5 hardware, otherwise it should use M3.
The M2.5 nut is 5mm across and 2mm high. The M3 nut is 5.5mm across and 2.4mm high. The corresponding screws are 2.5mm and 3mm in diameter, respectively. Also please keep a small 0.5mm gap between the holes and the screws and nuts.

I need the main scad file to be parametric, such as `qr200-landing-feet.scad` and I don't think I'll need additional files beyond that.
I'm not using any external libraries or anything like that. Thanks!

[Assistant]
qr200-landing-feet.scad:
```
/*
Quadrysteria QR200 landing feet  (c) by Jorge Monteiro

Quadrysteria QR200 landing feet is licensed under a
Creative Commons Attribution-ShareAlike 4.0 International.

You should have received a copy of the license along with this
work.  If not, see <http://creativecommons.org/licenses/by-sa/4.0/>.
*/
/* [Basics] */
// Set 0 to use M2.5 screw and nuts or 1 to use M3 screw and nuts
useM3nut=0;//[0:1]

/* [Ignore] */
$fn=40*1;
m25nut=5*1;
m25nuth=2*1;
m25screw=2.5*1;
m3nut=5.5*1;
m3nuth=2.4*1;
m3screw=3*1;
gap=0.5*1;

if (useM3nut==1) {
    echo("M3");
    screw=m3screw+gap;
    nut=m3nut+gap*3;
    nutHeight=m3nuth+gap*1.5;
    drawFoot(screw, nut, nutHeight);

} else {
    screw=m25screw+gap;
    nut=m25nut+gap*3;
    nutHeight=m25nuth+gap*1.5;

    drawFoot(screw, nut, nutHeight);
}

module drawFoot(screw, nut, nutHeight) {
    difference() {
        translate([0,0,-5])
        difference() {
            union(){
                cylinder(10, 5, 5);
                sphere(r=5);
            }
            cylinder(10, screw/2, screw/2);
        }
        // draw the nut hole
        cylinder(nutHeight, nut/2, nut/2, $fn=6);
        // and the openning to insert the nut
        translate([-nut/2,0,0]) cube([nut,nut,nutHeight]);

    }
};

```


[User]
I want to create a customizable design of interconnected triangles, each containing a polyline shape. I've seen a similar design and have some specific parameters in mind, but I'd like to make it parametric so I can easily tweak the overall size, shape, and detail.

Here's what I'm looking for:

1.  **Base Triangle Grid:** The design should be based on a grid of equilateral triangles. I need to control the number of triangles along the x and y axes, specifically called `x_triangles` and `y_triangles`.
2.  **Triangle Size:** The side length of each triangle should be adjustable via a parameter called `triangle_side_length`.
3.  **Polyline Shape:** Each triangle will contain a polyline. The coordinates of the polyline's vertices will be passed in as two strings, `xs` and `ys`, which will then be parsed into a list of x,y coordinates. I'd like the line width of this polyline to be adjustable with a `line_width` parameter.
4.  **Extrusion:** The entire design should be extruded to a certain height, controlled by a `height` parameter.
5.  **Symmetrical Polylines:** Each triangle will have a polyline drawn within it, and then rotated by 120 degrees twice, to generate a symmetrical shape within the triangle.
6.  **Staggered Triangles:** The grid should be arranged so that every other row is staggered, similar to a honeycomb pattern.
7.  **Virtual Triangles:** I want the polylines to generate the appearance of a triangle. Each triangle in the grid will have the same, reflected, polylines inside.

The initial values I want to use to test this are:
*   `xs = "0 1.5 1.5 -4 -6.5"`
*   `ys = "0 2.5 -6 -6 0"`
*   `triangle_side_length = 20`
*   `x_triangles = 10`
*   `y_triangles = 6`
*   `line_width = 0.5`
*   `height = 2`

Could you provide the OpenSCAD code to achieve this? I don't want to use any external libraries. Please make sure the code is well-commented so it's easy to understand and modify.

[Assistant]
symmetrical_virtual_triangle.scad:
```
xs = "0 1.5 1.5 -4 -6.5";
ys = "0 2.5 -6 -6 0";
triangle_side_length = 20;
x_triangles = 10;
y_triangles = 6;
line_width = 0.5;
height = 2;

//////////////
// 2D Drawing
//

// Draws a line between the points.
//
// Parameters:
//     point1 - the start point [x1, y1] of the line.
//     point2 - the end point [x2, y2] of the line.
//     width  - the line width.
//     cap_round - ends the line with round decorations that has a radius equal to half of the width of the line.
module line(point1, point2, width = 1, cap_round = true) {
    angle = 90 - atan((point2[1] - point1[1]) / (point2[0] - point1[0]));
    offset_x = 0.5 * width * cos(angle);
    offset_y = 0.5 * width * sin(angle);

    offset1 = [-offset_x, offset_y];
    offset2 = [offset_x, -offset_y];

    if(cap_round) {
        translate(point1) circle(d = width, $fn = 24);
        translate(point2) circle(d = width, $fn = 24);
    }

    polygon(points=[
        point1 + offset1, point2 + offset1,
        point2 + offset2, point1 + offset2
    ]);
}

// Creates a polyline, defined by the vector of the segment points.
//
// Parameters:
//     points - the coordinates of the polyline segments.
//     width  - the line width.
module polyline(points, width = 1, index = 0) {
    if(index < len(points)) {
        if(index == 0) {
            polyline(points, width, index + 1);
        } else {
            line(points[index - 1], points[index], width);
            polyline(points, width, index + 1);
        }
    }
}

///////////////////
// Turtle Graphics
//

// Creates a vector containing the current point [x, y] and the angle of the turtle.
// Parameters:
//     x - the x coordinate of the turtle.
//     y - the y coordinate of the turtle.
//     angle - the angle of the turtle.
// Return:
//     the turtle vector [[x, y], angle].
function turtle(x, y, angle) = [[x, y], angle];

// Moves the turtle forward the specified distanc
// Parameters:
//     turtle - the turtle vector [[x, y], angle].
// Return:
//     the new turtle vector after forwarding
function forward(turtle, length) =
    turtle(
        turtle[0][0] + length * cos(turtle[1]),
        turtle[0][1] + length * sin(turtle[1]),
        turtle[1]
    );

// Returns a new turtle vector with the given point.
// Parameters:
//     turtle - the turtle vector [[x, y], angle].
//     point - the new point of the turtle.
// Return:
//     the new turtle vector
function set_point(turtle, point) = [point, turtle[1]];

// Returns a new turtle vector with the given angle.
// Parameters:
//     turtle - the turtle vector [[x, y], angle].
//     angle - the angle of the turtle.
// Return:
//     the new turtle vector
function set_angle(turtle, angle) = [turtle[0], angle];

// Turns the turtle the specified number of degrees.
// Parameters:
//     turtle - the turtle vector [[x, y], angle].
//     angle - number of degrees.
// Return:
//     the new turtle vector
function turn(turtle, angle) = [turtle[0], turtle[1] + angle];

///////////////////
// Turtle Spirally
//

// Moves the turtle spirally. The turtle forwards the 'length' distance, turns 'angle' degrees, minus the length with 'step' and then continue next  spiral until the 'min_length' is reached.
// Parameters:
//     turtle - the turtle vector [[x, y], angle].
//     length - the beginning length.
//     decreasing_step - the next length will be minus with the decreasing_step.
//     angle - the turned degrees after forwarding.
//     ending_length - the minimum length of the spiral.
module spiral(turtle, length, decreasing_step, angle, ending_length = 10, line_width = 1) {
    if(length > ending_length) {
        turtle_after_forwarded = forward(turtle, length);
        line(turtle[0], turtle_after_forwarded[0], line_width);
        turtle_after_turned = turn(turtle_after_forwarded, angle);
        spiral(turtle_after_turned, length - decreasing_step, decreasing_step, angle, ending_length, line_width);
    }
}

////////////////////////////////
// Symmetrical virtual triangle

// Use the given points, draw a ployline. Repeat the previous step after rotate 120 and 240 degrees.
// Parameters:
//     points - the coordinates of the polyline segments.
//     line_width  - the line width.
module virtual_triangle(points, line_width = 1) {
    polyline(points, line_width);
    rotate([0, 0, 120]) polyline(points, line_width);
    rotate([0, 0, 240]) polyline(points, line_width);
}

// Draw a symmetrical virtual triangle.
// Parameters:
//     points - the coordinates of the polyline segments of a single virtual_triangle.
//     length - the length of a triangle side.
//     x_tris - the numbers of triangles in the x directions
//     y_tris - the numbers of triangles in the y directions
//     line_width  - the line width.
module symmetrical_virtual_triangle(points, length, x_tris, y_tris, line_width = 1) {
    height = length * sqrt(3) / 2;
    x_numbers = x_tris % 2 == 0 ? x_tris + 1 : x_tris;
    y_numbers = y_tris - 1; //(y_tris % 2 == 0 ? y_tris + 1 : y_tris) - 1;
    for(y_factor = [0 : y_numbers]) {
        for(x_factor = [0 : x_numbers]) {
            x_offset = length / 2 * x_factor + (y_factor % 2) * length / 2;
            y_offset = height * y_factor;

            if(x_factor % 2 == 0) {
                if(x_factor != 0 || y_factor % 2 != 0) {
                    translate([x_offset, y_offset, 0])
                        virtual_triangle(points, line_width);
                }
            } else {
                if(x_factor != x_numbers || y_factor % 2 == 0) {
                    translate([x_offset, height / 3 + y_offset, 0])
                    mirror([0, 1, 0])
                        virtual_triangle(points, line_width);
                }
            }
        }
    }
}

/* Examples
points1 = [
    [17.5, -10],
    [9.5, -10],
    [5, -2.5],
    [1.5, -2.5],
    [0, 0],
    [-1.5, -2.5],
    [-5, -2.5],
    [-9.5, -10],
    [-17.5, -10]
];

linear_extrude(2) symmetrical_virtual_triangle(points1, 35, 10, 4, 1);

points2 = [
    [10, -6],
    [-6.5, -6],
    [-3, 0],
    [0, 0]
];

linear_extrude(2) translate([225, 0, 0]) symmetrical_virtual_triangle(points2, 20, 10, 4, 0.5);

points3 = [
    [0, 0],
    [1.5, 2.5],
    [1.5, -6],
    [-4, -6],
    [-6.5, 0]
];

linear_extrude(2) translate([0, 200, 0]) symmetrical_virtual_triangle(points3, 20, 10, 4, 0.5);
*/

//////////////
// String API
//

// Returns a new string that is a substring of this string. The substring begins at the specified `begin` and extends to the character at index `end` - 1. Thus the length of the substring is `end` - `begin`.
// Parameters:
//     begin - the beginning index, inclusive.
//     end - the ending index, exclusive.
// Returns:
//     The resulting string.
function sub_string(string, begin, end, result = "") =
    end == undef ? sub_string(string, begin, len(string)) : (
        begin == end ? result : sub_string(string, begin + 1, end, str(result, string[begin]))
    );

// Splits this string around matches of the given delimiting character.
// Parameters:
//     string - the source string.
//     delimiter - the delimiting character.
function split(string, delimiter) =
        len(search(delimiter, string)) == 0 ? [string] : split_string_by(search(delimiter, string, 0)[0], string);

function split_string_by(indexes, string, strings = [], i = -1) =
    i == -1 ? split_string_by(indexes, string, [sub_string(string, 0, indexes[0])], i + 1) : (
        i == len(indexes) - 1 ? concat(strings, sub_string(string, indexes[i] + 1)) :
                split_string_by(indexes, string, concat(strings, sub_string(string, indexes[i] + 1, indexes[i + 1])), i + 1)
    );

// Parse string to number
// Parameters:
//     string - the source string.
// Results:
//     The resulting number.
function parse_number(string) = string[0] == "-" ? -parse_positive_number(sub_string(string, 1, len(string))) : parse_positive_number(string);

function str_num_to_int(num_str, index = 0, mapper = [["0", 0], ["1", 1], ["2", 2], ["3", 3], ["4", 4], ["5", 5], ["6", 6], ["7", 7], ["8", 8], ["9", 9]]) =
    index == len(mapper) ? -1 : (
        mapper[index][0] == num_str ? mapper[index][1] : str_num_to_int(num_str, index + 1)
    );

function parse_positive_int(string, value = 0, index = 0) =
    index == len(string) ? value : parse_positive_int(string, value * pow(10, index) + str_num_to_int(string[index]), index + 1);

function parse_positive_decimal(string, value = 0, index = 0) =
    index == len(string) ? value : parse_positive_decimal(string, value + str_num_to_int(string[index]) * pow(10, -(index + 1)), index + 1);

function parse_positive_number(string) =
    len(search(".", string)) == 0 ? parse_positive_int(string) :
        parse_positive_int(split(string, ".")[0]) + parse_positive_decimal(split(string, ".")[1]);

// for MakerBot Customizer
function strs_to_points(xs, ys, points = [], index = 0) =
    index == len(xs) ? points : strs_to_points(xs, ys, concat(points, [[parse_number(xs[index]), parse_number(ys[index])]]), index + 1);

linear_extrude(height)
    symmetrical_virtual_triangle(strs_to_points(split(xs, " "), split(ys, " ")), triangle_side_length, x_triangles, y_triangles, line_width);

```


[User]
Hey! I'm looking to get a design for a door holder, like in that YouTube video I saw (https://youtu.be/8KOF2k5yOLM). The basic idea is two parts: a hook that mounts to the door frame and a retaining plate that mounts to the door itself. When the door swings open, it catches on the hook and is held in place.

I need this to be parametric, so I can easily adjust the size and spacing to fit different door setups. Here are some specifics:

**Hook:**

*   It'll have a flat mounting plate with two mounting holes.
*   Attached to the mounting plate will be a perpendicular "arm" that sticks out from the door frame.
*   At the end of the arm, there should be a little angled bit that the retaining plate catches on.
*   I'd like to control the length of the arm and the mounting plate dimensions, and the diameter and locations of the holes. Also the thickness of all parts needs to be adjustable.
*  The angled bit should also be parametric.

**Retaining Plate:**

*   It'll have a flat mounting plate with two mounting holes. This should also be adjustable in size and hole position and diameter.
*   It should be designed to fit snugly into the hook.
*   The thickness should be parametric

I'd like all the dimensions to be variables at the top of the scad file. I'm hoping to have parameters like:

*   `length`: the length of the hook arm
*   `a1`: mounting plate height
*   `a2`: mounting plate width
*   `thick`: overall thickness
*  `hole`: mounting hole diameter

And of course any other parameters you need to generate it.

I don't have a specific library in mind, but if you know of one that could make this easier, that'd be awesome. I'm thinking something like BOSL2 would be great. Thanks!

[Assistant]
doorholder.scad:
```
$fn=30;
length=40;
a1=20;
a2=26;
thick=5;
hole=5; //mounting holes diameter

//hook
rotate([90,0,0]) {
    //mounting plate
    difference() {
        cube([a2,a1,thick]);
        translate([a2/3,hole,-thick/4])
        cylinder(thick*2,hole/2,hole/2);
        translate([a2/3,a1-hole,-thick/4])
        cylinder(thick*2,hole/2,hole/2);
    }
    //up
    translate([a2-thick,0,0]) {
        cube([thick,a1,length]);
        //edge
        movex=sqrt(2*thick)/2;
        translate([movex,0,length-thick]) {
            rotate([0,45,0]) {
                cube([thick,a1,thick]);
            }
        }
    }
}

//retaining plate
translate([-a2-10,0,0]) {
    difference() {
        cube([a2,a1,thick]);
        translate([a2/3,hole,-2])
        cylinder(thick*2,hole/2,hole/2);
        translate([a2/3,a1-hole,-2])
        cylinder(thick*2,hole/2,hole/2);
    }
}

```


[User]
I'd like a parametric OpenSCAD design for a sliding lid box. The lid should be captive, meaning it's held in place by a groove or ridge so it can't come completely off.

Here are the key features I need:

*   **Parametric Dimensions:** I need to be able to control the box's width (X), length (Y), and height (Z), as well as the wall, lid, and floor thicknesses.
*   **Sliding Lid:** The lid should slide along the length (Y) of the box.  There should be a small amount of "play" to ensure it slides smoothly, and I'd like to be able to adjust this.
*   **Lid Retention:** The lid needs to stay on, so it can't just slide all the way off in either direction. A ridge or groove system is fine for that.
*   **Optional Label Recess:** I'd like an option to include a recessed area on the lid for a label, probably in the center.
*   **Optional Fingernail Grip:** I need an option for a small recess on the lid to help with opening it.
*   **Internal Dividers:** It should be easy to specify the number of dividers on the X and Y axis.
*   **Preview Option:** I'd like a way to preview the box with the lid partially slid open.

I'm not using any specific libraries now, so if any would help, I'm open to suggestions.

Could you provide the necessary OpenSCAD code?

[Assistant]
sliding_lid_box.scad:
```
// Box builder with angled sliding lid.
// Has potential recess for label and fingernail grip to open.

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

// Width of Box
Box_x = 172;
// Length of Box
Box_y = 41;
// Height of Box
Box_z = 25;
Wall_thickness = 2;  //[2:0.5:10]
Floor_thickness = 1;
Lid_thickness = 3;   //[2:0.5:10]
// Add inset for a Label
Lid_inset = "yes"; // [yes,no]
// Fingernail recess to ease opening lid
Grip_recess = "yes"; // [yes,no]

// Amount of play in the lid to fit well.
Looseness = 0.8;  //[0:0.1:1]
//
Show_assembled = "no"; // [yes,no]
// Number of internal dividers
X_Dividers = 3;
// Number of internal dividers
Y_Dividers = 0;

/* [Hidden] */
//Lid scale (effective overhang)
lid_scale = 0.985;
fingernail_width = 0.5;

Delta = 0.1;     // to get good overlaps for real objects
roundness = 32;  // for curve res.
epsilon = 0.001; // for minkowski lid height :(

// Lid
module lid(extra_x=Looseness, extra_z=Delta*2) {
	translate([0,Wall_thickness/2+Delta,-Lid_thickness/2-epsilon/2])
	linear_extrude(height=Lid_thickness+extra_z*2, scale=[lid_scale,1])
		square(size=[Box_x+extra_x,Box_y], center=true);
}

// undercut ridge on top of box to hold lid
module lid_ridge(x,y) {
	difference() {
		// the surround
		minkowski(){
			cube(size=[x,y,Lid_thickness], center=true);
			cylinder(h=epsilon, r=Wall_thickness, $fn=roundness); // increases lid height by 2*epsilon
		}
		// minus the lid
		lid(0, epsilon);
	}
}

// helper hole to open lid.
module fingernail_helper() {
	#cube(size=[Box_x/3, fingernail_width,Lid_thickness], center=true);
}

// box
module box() {
	x = Box_x-Wall_thickness;
	y = Box_y-Wall_thickness;
	linear_extrude(height=Box_z-Lid_thickness, convexity=4)
	difference() {
		offset(r=Wall_thickness, $fn=roundness)
			square(size=[x, y], center=true);
		square(size=[x, y], center=true);
	}
	// floor
	translate([0,0,Floor_thickness/2])
		cube(size=[x+Delta, y+Delta,Floor_thickness],center=true);
	// Top ridge of lid
	translate([0,0,Box_z-Lid_thickness/2])
		lid_ridge(x,y);
}

//-------------------
// Build the box
box();
if (X_Dividers > 0) {
	div_step = Box_x / (X_Dividers+1);
	for (i=[1:X_Dividers]) {
		translate([Box_x/2-div_step*i, -Wall_thickness/4, Box_z/2-Lid_thickness/2])
			cube(size=[Floor_thickness, Box_y, Box_z-Lid_thickness], center=true);
	}
}
if (Y_Dividers > 0) {
	div_step = Box_y / (Y_Dividers+1);
	for (i=[1:Y_Dividers]) {
		#translate([-Wall_thickness/4, Box_y/2-div_step*i, Box_z/2-Lid_thickness/2])
			cube(size=[Box_x, Floor_thickness, Box_z-Lid_thickness], center=true);
	}
}
// Build the Lid
tz = (Show_assembled == "yes") ? Box_z-Lid_thickness/2 : Lid_thickness/2;
tx = (Show_assembled == "no") ? Box_x+Wall_thickness  : 0;
ty = (Show_assembled == "yes") ? Box_y/3  : 0;
lid_color = (Show_assembled == "yes") ? [0.7,0.7,0] : 0;
color(lid_color)
translate([tx,ty,tz])
	difference() {
		lid(-Looseness,0);
		// subtract fingernail recess
		if (Grip_recess=="yes") {
			translate([0,-Box_y/2.7,Lid_thickness/4])
				fingernail_helper();
		}
		// subtract inset
		if (Lid_inset=="yes") {
			translate([0,0,Lid_thickness/2])
				cube(size=[Box_x/2, Box_y/2,Lid_thickness/2], center=true);
		}
	}

```


[User]
Hey, I'm looking to create a holder for an inductive sensor for my 3D printer's auto bed leveling system. The current holder I have is working okay, but it's a bit clunky and I want to make it more customizable and robust.

Specifically, the design should incorporate a base with mounting holes for the printer carriage, a sensor mount, and a connecting arm. The sensor mount should have a customizable diameter to fit various cylindrical inductive sensors, specifically targeting one with a diameter of 12.2mm but allowing for a range. I'd like to be able to specify that diameter as a variable.

The base should be relatively flat, and have four mounting holes in a square pattern. I have the positions for these in the previous design: they're centered at +/- 12mm in both x and y from the base center. The mounting holes should be counterbored for M3 screws, with a 5.5mm diameter hole and a 2.1mm deep counterbore.

The sensor mount will be a cylindrical 'tube' attached to the base via the arm, with the sensor hole running along the cylinder's axis. I'd like the height of the cylinder to be parameterized so that I can ensure the sensor is flush (or can be made flush) with the base of the holder when installed. The cylinder part is located about 11mm from the base.

The arm connecting the base and the mount needs to be fairly strong. In the old design this was done with a hull between a cylinder and a cube, but perhaps there's a more elegant solution. It should angle slightly towards the base, as the sensor needs to move away from the extruder.

In addition to the base, arm, and sensor mount, there needs to be a bracket that stabilizes the whole thing. This bracket attaches at the base and the arm using a cube and a simple extruded polygon, and it should be adjustable in length from the base.

Finally, the overall design needs to be parametric enough that I can adjust dimensions to suit any future sensor or mounting changes. It would be ideal if you could create an OpenSCAD file or set of files that I could use for that. I'm not concerned about libraries right now, but I might use one in the future so knowing that would be useful.

Let me know if you need any other info or if this isn't clear.

[Assistant]
holder3.scad:
```

// diameter of sensor hole
sensorHoleDiameter = 12.2; // [3:0.1:19]

// quality
$fn=100;

module vetrak() {
  difference() {
    linear_extrude (height=3)
    difference() {
      union() {
        difference() {
          square (32, center=true);
          circle (d=27);
        }
      }

      translate ([-12,-12]) circle (d=3.2);
      translate ([12,-12]) circle (d=3.2);
      translate ([-12,12]) circle (d=3.2);
      translate ([12,12]) circle (d=3.2);
    }

    translate ([-12,-12,1]) cylinder (d=5.5, h=2.1);
    translate ([12,-12,1]) cylinder (d=5.5, h=2.1);
    translate ([-12,12,1]) cylinder (d=5.5, h=2.1);
    translate ([12,12,1]) cylinder (d=5.5, h=2.1);
  }
}

module zaves() {
  translate ([0.5,0,0])
  difference() {
    hull() {
      translate ([11,0,0]) cylinder (h=6, d=32);
      translate ([-3,0,3]) cube ([1, 32, 6], center=true);
    }
    translate ([11,0,-0.1]) cylinder (h=6.2, d=sensorHoleDiameter);
  }
}

module main() {
  union() {
    union() {
      vetrak();
      translate ([0,-19,1.5]) cube ([32, 6, 3], center=true);
      translate ([0,-16, 6]) rotate ([0,270,90]) zaves();
    }

    difference() {
      translate ([16,-8.1,3])
      rotate ([90,0,270])
      linear_extrude (height=32)
      polygon (points=[[0,0], [10, 0], [10, 5.2]]);

      translate ([-12,-12,1]) cylinder (d=5.5, h=12);
      translate ([12,-12,1]) cylinder (d=5.5, h=12);
      cylinder (d=27, h=12);
    }
  }
}

main();

```


[User]
I need a custom bracket designed for a dog door, specifically to hold the slider in place. I've been using a basic design, but I need it to be fully parametric so I can easily adjust the dimensions. The bracket should have a base, a side wall, and an overhanging lip, and screw holes for mounting. Here are the specific parameters I need to control:

*   **Bracket Width:** This is the overall width of the bracket base.
*   **Bracket Length:** This is the overall length of the bracket, and should define the length of all three parts.
*   **Door Gap:** This is the vertical gap between the base and the lip, specifically where the door slider will sit.
*   **Sidewall Thickness:** The thickness of the vertical sidewall.
*  **Screw Diam Top:** The diameter of the top part of the screw hole.
*  **Screw Diam Bottom:** The diameter of the bottom part of the screw hole.
*  **Screw Head Depth:** This defines the total depth of the countersunk part of the screw hole.

The bracket will have a flat base, a vertical sidewall coming up from one side of the base, and then a horizontal lip overhanging this wall. The base should be the same length as the sidewall, and have a thickness equivalent to the *screw\_head\_depth*. The sidewall height should be equal to the *screw\_head\_depth* + *door\_gap* + 0.2. The lip should extend the length of the side, with a width of *sidewall\_thickness* + 4, and have the thickness equal to the *sidewall\_thickness*.

I need two counter-sunk screw holes on the base, and they should be accessible on the flat bottom. One hole should be at 1/3rd of the length from one end, and another at 2/3rds. Both should have their width positioned at 3/5th of the total width.

Could you create the OpenSCAD file(s) for this design? Please use the *difference* operation to cut the screw holes.

[Assistant]
dog_door_bracket-customizer.scad:
```
// Bracket Width
width = 15; // [13:100]

// Bracket Length
length = 26; // [13:100]

// Gap for Door Slider
door_gap = 3.5;

// Bracket sidewall thickness
thickness = 2;

screw_diam_top = 5.7;
screw_diam_bot = 2.9;
screw_head_depth = 2.5;

thickness_bot = screw_head_depth;
side_h = thickness_bot + door_gap + 0.2;

module screw_hole(d1,d2,th) {
    cylinder ($fn=30,d1=d1, d2 = d2, h=th+1);
}

rotate(a = [0, -90, 0]) {
    difference () {
        union() {
            cube ([width,length,thickness_bot]);
            cube ([thickness,length,side_h]);
            translate ([0,0,side_h]) cube ([thickness+4,length,thickness]);
        }
        translate ([width*3/5,length/3,-0.5]) screw_hole(screw_diam_bot,screw_diam_top,thickness_bot);
        translate ([width*3/5,length*2/3,-0.5]) screw_hole(screw_diam_bot,screw_diam_top,thickness_bot);

    }
}
```


[User]
I'd like to design a pen holder that looks like a sword. It should also function as a bookmark. I have some ideas about the different parts and how they fit together, but I'd like the design to be parametric, so it can be adjusted for different pen sizes and sword dimensions.

Here's how I'm envisioning it:

The main part is the sword blade, a rectangular section. I'd like to control the length, width, and thickness of this part using parameters. The end of the blade should have a pointy tip, created using a rotated cube.

Then there is the hilt, which should consist of a guard, grip, and pommel. The guard should be a small, thin rectangle perpendicular to the blade. The grip will be a small rectangle parallel to the blade. The pommel should be a small cylinder at the end of the grip. All of these dimensions should be derived from the blade width to keep them proportional, but there should be options to adjust these as well.

Finally, there's the pen holder, which should be a hollow cylinder. The inside diameter needs to be adjustable to fit different pens, and the wall thickness should also be configurable. I want the cylinder to be located offset from the side of the sword blade near the guard and have an opening for the pen to easily slide in. This opening should be created by subtracting a rotated cube.

I also want to control the overall smoothness of the curves using the `$fn` variable.

Ideally, the code should be organized in modules, making it easy to modify. The main module should call all the other modules to generate the full sword pen holder.

Could you generate a parametric OpenSCAD file or files to implement this design?  I don't think you'll need any special libraries for this.

[Assistant]
SwordPennHolder.scad:
```
//
//  SwordPenHolder.scad
//
//  EvilTeach
//
//  12/19/2015
//
//  This models a simple bookmark with a pen holder.
//
//  ABS with no raft, no support.  Layer .25 no shells
//
//     True, This! —

//     Beneath the rule of men entirely great
//     The pen is mightier than the sword. Behold
//     The arch-enchanters wand! — itself is nothing! —
//     But taking sorcery from the master-hand
//     To paralyse the Cæsars, and to strike
//     The loud earth breathless! — Take away the sword —
//     States can be saved without it!

// This is the diameter of the pen that should fit into the holder
penDiameter         =  11.5;

// This is the length of the rectangular portion of the sword
swordBladeLength    = 100.0;

// This is the width of the rectangluar potion of the sword, which most of the hilt is based off of
swordBladeWidth     =  20.0;

// This is the thickness of the sword blade.  It is flexible.  I recommend keeping it greater than 1.
swordBladeThickness =   1.3;

// Draws just the rectangular part of the sword.
module blade()
{
    color("cyan")
        cube([swordBladeLength, swordBladeWidth, swordBladeThickness]);
}

// Draws the pointy tip of the sword.
module point()
{
    triangleLegs = sqrt(swordBladeWidth * swordBladeWidth / 2);
    color("lime")
        translate([swordBladeLength, 0, 0])
            rotate([0, 0, 45])
                cube([triangleLegs, triangleLegs, swordBladeThickness]);
}

// Draws a simple hilt, based on the sword width
module hilt()
{
    color("pink")
    {
        // Guard
        rotate([0, 0, 90])
        translate([-swordBladeWidth, 0, 0])
        cube([swordBladeWidth * 3, swordBladeWidth / 4, swordBladeThickness]);

        // Grip
        translate([-swordBladeWidth / 4,
                   swordBladeWidth / 4,
                   0])
        rotate([0, 0, 90])
        cube([swordBladeWidth / 2, swordBladeWidth, swordBladeThickness]);

        // Prommel
        translate([-swordBladeWidth * 3/2, swordBladeWidth / 2, 0])
            cylinder(r = swordBladeWidth / 3, swordBladeThickness);
    }
}

// This just groups the related pieces into one call.
module sword()
{
    point();
    blade();
    hilt();
}

// Reusable draw a hollow cylinder
module hollow_cylinder(outsideRadius, insideRadius, height)
{
    difference()
    {
        cylinder(r = outsideRadius, h = height);
        cylinder(r = insideRadius,  h = height);
    }
}

// This draws just the pen holder
module pen_holder()
{
    // This is the thickness of the pen holder.  Too Thin and it breaks.  To Thick and it cracks.
    thickness = 1.1;

    // The inside radius needs to hold the pen
    insideRadius  = penDiameter / 2;

    // The outside needs to be big enough to hold it, yet remain flexible.
    outsideRadius = insideRadius + thickness;

    color("yellow")
        // Move to the correct location relative the blade
        translate([outsideRadius / 2 + swordBladeWidth / 3,
                   swordBladeWidth,
                   outsideRadius + swordBladeThickness - thickness])

            // give the cylinder the correct orientation
            rotate([90, 0, 0])
            difference()
            {
                hollow_cylinder(outsideRadius,
                                outsideRadius - thickness,
                                swordBladeWidth);

                // Subtrace out an entrance for the pen.
                rotate([0, 0, 45])
                    cube([outsideRadius, outsideRadius, swordBladeWidth]);
            }
}

// This is the entry point
module main()
{
    // I prefer smooth where possible.
    $fn=360;

    sword();
    pen_holder();
}

// From the perspective of an old C programmer.
main();

```


[User]
I'm looking to design a customizable keycap that I can 3D print. My girlfriend wants a way to tell her keys apart, and a little keycap seems like a good way to go. I'd like it to be parametric so I can easily change the size and other aspects later on.

Here's what I'm thinking it should look like:

*   **Basic Shape:** It should be a cylindrical cap that fits over the head of a standard key.
*   **Dimensions:**
    *   The keycap's diameter needs to be adjustable, let's call this `keydiam`.
    *   The overall thickness of the cap (including top and bottom walls) should be `keythickness`.
    *   The walls of the cap should have a thickness `wall`.
*   **Ring Hole:** There should be a hole to attach a keyring.
    *   The hole should be circular, with a diameter `holesize`.
    *   The distance from the edge of the keycap to the edge of this hole is `holeoutoffset`.
*   **Text:** I'd like to engrave or extrude text on the side of the keycap.
    *   The text should be a string variable: `text`.
    *   The size of the text should be controlled by `textsize`.
    *   The text can either be extruded outwards from the surface or engraved inward, determined by the `textdepth` variable (positive for extrusion, negative for engraving).
    *   There should be a vertical offset of the text from the middle of the cap that can be adjusted with `textoffset`.
*   **Smoothing:** I'd like to use `$fn` to control the number of segments for the curves.

I'm thinking the design should be able to handle both text that sticks out *and* text that's recessed into the surface. Also, the cylinder has to be slightly larger than the key so that it will fit, and there should also be a cutout to make it easier to attach and detach the keycap from the key. I think a cube-shaped cutout would work the best for that.

Could you generate the OpenSCAD code for this? I'd prefer it if it didn't rely on any external libraries.

[Assistant]
keycap.scad:
```
// diameter of the key
keydiam = 24;

// thickness of the key
keythickness = 2.5;

// wall thickness of the key cap
wall=0.8;

// diameter of the hole for the ring
holesize=6;

// offset of the edge of the hole from the edge of the key
holeoutoffset = 3.2;

// smoothing
$fn=60;

// Text on the side of the key
text="Home";

// Font size of the text
textsize=5;

// Depth of the text. Positive depth will stand out of the cap, negative will be embosed into the cap
textdepth=-0.1;

// Vertical offset of the text
textoffset=1.5;

rotate([-90,0,180]) {
  difference() {
    difference() {
      difference() {
          if (textdepth>=0) {
            union() {
              cylinder(r1=keydiam/2+wall, r2=keydiam/2+wall,h=keythickness+2*wall, center=true);
              rotate([0,0,180]) translate([0,-textoffset,keythickness/2+wall]) {
                linear_extrude(height=textdepth, center=false) {
                  text(text, size=textsize, halign="center", valign="center");
                }
              }
            }
         } else {
           difference() {
              cylinder(r1=keydiam/2+wall, r2=keydiam/2+wall,h=keythickness+2*wall, center=true);
              rotate([0,0,180]) translate([0,-textoffset,keythickness/2+wall+textdepth]) {
                linear_extrude(height=-2*textdepth, center=false) {
                  text(text, size=textsize, halign="center", valign="center");
                }
              }
            }
         }

         cylinder(r1=keydiam/2, r2=keydiam/2,h=keythickness, center=true);
      }

      translate([0,(keydiam+2*wall)*1.25/2*1.45,0]) {
        cube(size=[1.25*(keydiam+2*wall), 1.25*(keydiam+2*wall), (keythickness+2*wall)*1.25], center=true);
      }
    }

    translate([0,-1 * (keydiam/2 - holesize/2-holeoutoffset),0]) {
      cylinder(r1=holesize/2, r2=holesize/2, h=2*(keythickness+2*wall), center=true);
    }
  }
}

```


[User]
Hey, I'm looking for help designing a parametric moose cup holder in OpenSCAD. I've got a basic idea and some code that I've been playing with, but it's a bit rough, and I need it to be more robust and easier to customize. I'm aiming for something that can be printed in multiple parts, which is important because I want to be able to print it on smaller printers.

The basic concept is a cup holder ring with two moose antlers attached to the sides. The main thing is that the cup holder should be sized to fit a specific cup, which will vary, so it needs to be fully parametric. The cup itself is basically a truncated cone, so it should have a different top and bottom diameter along with a height parameter.  I also want options to show or hide the cup holder/ring and each of the antlers independantly so I can print individual parts when I need them.

So, to recap:

1.  **Cup Holder:**
    *   It needs to take the top diameter of the cup, the bottom diameter of the cup, and the height as parameters.
    *   The cup holder should be slightly larger than the cup itself, maybe with a 2mm or so tolerance all around to ensure it fits comfortably. The cylinder should be slightly taller than the cup so there is a bit of extra room at the top and bottom (say 10% of the cup height or so).
    *   I want to cut some slots in the side to allow for things to flow through and make it look a bit more interesting.
2. **Antlers:**
    *   I have a DXF file for the antler shape. It should be scaled and positioned correctly on the side of the cup holder. They need to be mirrored so there is one on either side of the cup holder. I don't want the base of the antlers to be inside of the cup, so they need to be cut out of the model.
    *   Each antler should be able to be shown/hidden separately.
3.  **Customization:**
    *   All the dimensions should be customizable.
    *   I should be able to choose to show or hide the cup holder and the antlers individually.

I'd like to be able to generate STL files for the cup holder and the antlers separately. I'm thinking this might be better as multiple OpenSCAD files.

I'm not familiar with any OpenSCAD libraries, but I'm open to using them if they make things easier. Just let me know which ones you're using and where to find them.

Can you help me build this?

[Assistant]
moose-cup_holder.scad:
```
// Customizable Moose Cup Holder
// by edak

//CUSTOMIZER VARIABLES
/* [Basic Options] */
// Cup top diameter (mm)
cup_top=70;
// Cup bottom diameter (mm)
cup_bottom=55;
// Cup height (mm)
cup_height=85;
// Show cup holder/ring?
show_holder="yes";// [yes,no]
// Show antler?
show_antler1="yes";// [yes,no]
// Show antler?
show_antler2="yes";// [yes,no]

module tapered_extrude_children(scale1=10.0,scale2=1.0,height=10,twist=45,fn=32) {
  for(kid=[0:$children-1]) {
    for(i=[0:fn-1]) translate([0,0,height*i/fn]) {
      hull() {
        linear_extrude(height=height/fn/10) scale(scale1*(fn-i)/fn+scale2*i/fn) child(kid);
        translate([0,0,height/fn-height/fn/10])
          linear_extrude(height=height/fn/10) scale(scale1*(fn-i-1)/fn+scale2*(i+1)/fn) child(kid);
      } // hull
    } // for each slice
  } // for each child
} // module

module cup() {
   translate([0,0,-cup_height/2]) cylinder(cup_height, cup_bottom/2,cup_top/2,true, $fn=100);
} // visualise the cup so we can subtract it such that it fits snugly

module slot() {
    translate([-(cup_top/2 + 3.5),0,-16.5]) rotate([0,atan(((cup_top - cup_bottom)/2)/cup_height),180]) difference(){
        translate([-5,-6,0.1]) cube([7,12,14])   ;
        tapered_extrude_children(scale1=1,scale2=0.8,height=12,fn=3) {
            circle(5.5,$fn=3);
        }
    }
} // needed some slots so made a box and took a tapered section out

module holder() {
    difference(){
        union(){
            intersection(){
                translate([0,0,-(cup_height/2)]) cylinder((cup_height-0.1), (cup_bottom+4)/2,(cup_top+4)/2,true, $fn=100);
                translate([0,0,-10]) cylinder(20.1,cup_top,cup_top,true);
            }
            slot();
            rotate([0,0,180]) slot();
        }
        cup();
    }
}

module antler() {
    difference(){
        translate([-((0.5*cup_top)+105),0,-13.8]) rotate([90,10,0]) scale([0.115,0.115,1]) linear_extrude(height = 8, center = true, convexity = 10) import("antler.dxf",convexity = 1);
        scale([1,0.95,1]) slot();
        holder(); //dont want to have the back end of the antler in the cup or holder!
        cup();
    }
}

if(show_antler1=="yes") antler();
if(show_antler2=="yes") rotate([0,0,180]) antler();
if(show_holder=="yes") holder();
//#cup();

```


[User]
Hi, I'm looking to create a customizable socket organizer in OpenSCAD. I want to be able to specify the sizes of the sockets I have, and have it generate a grid of holders with the sizes labeled. I've got both metric and imperial sockets, and would like to be able to mix them as needed.

Specifically, I'd like the design to be based on a base unit. The base should be a cube with a square hole for the socket to fit in. It should also have a small post that the socket clicks onto. The socket size itself should be adjustable based on measurements, and the size should be engraved/written on the top and all 4 sides.

I want to be able to specify an array of 8 of these base units, each with their own socket size, which can be mixed metric and imperial. I want a dropdown list of pre-set common sizes (metric: 4mm, 4.5mm, 5mm, 5.5mm, 6mm, 7mm, 8mm, 9mm, 10mm, 11mm, 12mm, 13mm; imperial: 5/32, 3/16, 7/32, 1/4, 9/32, 5/16, 11/32, 3/8, 13/32, 7/16, 15/32, 1/2). The actual hole size will need to be driven by variables that are different than the label.

Could you write this for me?  I'm also hoping it could use the `write` module from the `Write.scad` library to label each size on all four sides of the base and the top. The bases should fit together in an 8-block 2x4 pattern, spaced consistently.

Thanks!

[Assistant]
SocketOrganizer_-_Thingiverse_Version.scad:
```
use <write/Write.scad>;

/* [Array] */
SOCKET01 = 20; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]
SOCKET02 = 21; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]
SOCKET03 = 22; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]
SOCKET04 = 23; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]
SOCKET05 = 24; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]
SOCKET06 = 25; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]
SOCKET07 = 26; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]
SOCKET08 = 27; // [0:"None", 1:"4mm", 2:"4.5mm", 3:"5mm", 4:"5.5mm", 5:"6mm", 7:"7mm", 8:"8mm", 9:"9mm",10:"10mm",11:"11mm",12:"12mm",13:"13mm", 20:"5/32", 21:"3/16", 22:"7/32", 23:"1/4", 24:"9/32", 25:"5/16", 26:"11/32", 27:"3/8", 28:"13/32", 29:"7/16", 30:"15/32", 31:"1/2"  ]

/* [Hidden] */
THICKNESS = 10;
BASE = 21;

XX = 0;  //No Print

M40 = 1;
M45 = 2;
M50 = 3;
M55 = 4;
M60 = 5;
M65 = 6;
M70 = 7;
M80 = 8;
M90 = 9;
M10 = 10;
M11 = 11;
M12 = 12;
M13 = 13;
//M14 = 14;
//M15 = 15;

U05 = 20;
U06 = 21;
U07 = 22;
U08 = 23;
U09 = 24;
U10 = 25;
U11 = 26;
U12 = 27;
U13 = 28;
U14 = 29;
U15 = 30;
U16 = 31;
//U17 = 32;
//U18 = 33;

/* Metric Diameters */

// exact inch measurements from calipers

METRIC4 = 0.450;
METRIC4_5 = 0.452;
METRIC5 = 0.455;
METRIC5_5 = 0.470;
METRIC6 = 0.475;
METRIC6_5 = 0.450; // From a different set?
METRIC7 = 0.475;
METRIC8 = 0.475;
METRIC9 = 0.505;
METRIC10 = 0.550;
METRIC11 = 0.625;
METRIC12 = 0.650;
METRIC13 = 0.662;
//METRIC14 = 0.775; // too large for current base
//METRIC15 = 0.863; // too large for current base

/* US Diameters */
// US Variables as x/32
US05 = 0.450;   //  5/32
US06 = 0.454;   //  3/16
US07 = 0.464;   //  7/32
US08 = 0.460;   //   1/4
US09 = 0.470;   //  9/32
US10 = 0.538;   //  5/16
US11 = 0.548;   // 11/32
US12 = 0.538;   //   3/8
US13 = 0.545;   // 13/32
US14 = 0.625;   //  7/16
US15 = 0.620;   // 15/32
US16 = 0.666;   //   1/2
//US17 = 0.700;   // 17/32  // too large for current base
//US18 = 0.775;   //  9/16 // too large for current base

// 4 x 2 array
translate([-3*BASE,-BASE,0])
 customSocket(SOCKET01);
translate([-3*BASE,BASE,0])
 customSocket(SOCKET02);
translate([-BASE,-BASE,0])
 customSocket(SOCKET03);
translate([-BASE,BASE,0])
 customSocket(SOCKET04);
translate([BASE,-BASE,0])
 customSocket(SOCKET05);
translate([BASE,BASE,0])
 customSocket(SOCKET06);
translate([3*BASE,-BASE,0])
 customSocket(SOCKET07);
translate([3*BASE,BASE,0])
 customSocket(SOCKET08);

module customSocket(choice)
{
 if(choice == XX) {}
 else if(choice == M40) {fullSocket(METRIC4, "4mm");}
 else if(choice == M45) {fullSocket(METRIC4_5, "4.5mm");}
 else if(choice == M50) {fullSocket(METRIC5, "5mm");}
 else if(choice == M55) {fullSocket(METRIC4_5, "5.5mm");}
 else if(choice == M60) {fullSocket(METRIC6, "6mm");}
 else if(choice == M65) {fullSocket(METRIC6_5, "6.5mm");}
 else if(choice == M70) {fullSocket(METRIC7, "7mm");}
 else if(choice == M80) {fullSocket(METRIC8, "8mm");}
 else if(choice == M90) {fullSocket(METRIC9, "9mm");}
 else if(choice == M10) {fullSocket(METRIC10, "10mm");}
 else if(choice == M11) {fullSocket(METRIC11, "11mm");}
 else if(choice == M12) {fullSocket(METRIC12, "12mm");}
 else if(choice == M13) {fullSocket(METRIC13, "13mm");}
// else if(choice == M14) {fullSocket(METRIC14, "14mm");}
// else if(choice == M15) {fullSocket(METRIC15, "15mm");}
 else if(choice == U05) {fullSocket(US05, "5/32");}
 else if(choice == U06) {fullSocket(US06, "3/16");}
 else if(choice == U07) {fullSocket(US07, "7/32");}
 else if(choice == U08) {fullSocket(US08, "1/4");}
 else if(choice == U09) {fullSocket(US09, "9/32");}
 else if(choice == U10) {fullSocket(US10, "5/16");}
 else if(choice == U11) {fullSocket(US11, "11/32");}
 else if(choice == U12) {fullSocket(US12, "3/8");}
 else if(choice == U13) {fullSocket(US13, "13/32");}
 else if(choice == U14) {fullSocket(US14, "7/16");}
 else if(choice == U15) {fullSocket(US15, "15/32");}
 else if(choice == U16) {fullSocket(US16, "1/2");}
// else if(choice == U17) {fullSocket(US17, "17/32");}
// else if(choice == U18) {fullSocket(US18, "9/16");}
 else           {}

}

module fullSocket(measurement, nameText)
{
 translate([0,0,THICKNESS/2])
 difference()
 {
  socket(measurement);
  union()
  {
   name(nameText);
   rotate([0,0,90]) name(nameText);
   rotate([0,0,180]) name(nameText);
   rotate([0,0,-90]) name(nameText);
  }
 }
}
module name(sizeText)
{
  translate([0,-BASE/2,1.5])
   rotate([90,0,0])
    write(
        sizeText,
        h=5,
        t=1,
        font="Letters.dxf",
        center=true);
}

module socket(diameter)
{
  union()
  {
    difference()
    {
      base();
      disc(diameter);
    }
    post();
  }
}

module post()
{
    cylinder(d1=6.8, d2=6.6, h=9.5, $fn=20);
}

module disc(diameter)
{
    translate([0,0,THICKNESS/4])
     cylinder(d=diameter*25.4+1, h=THICKNESS/2, center=true);
}

module edge()
{
 translate([BASE/2, -BASE/2, -THICKNESS/2])
  polyhedron(
   points=[ [0,0,0],[0,0,3],[3,0,0],
            [0,BASE,0],[0,BASE,3],[3,BASE,0]  ],
   faces=[ [0,1,2],[4,1,0,3],[1,4,5,2],[0,2,5,3],
           [5,4,3] ]
 );
}

module base()
{
    union()
    {
      cube([BASE, BASE, THICKNESS], center=true);
      edge();
      mirror([1,0,0]) edge();
    }
}
```


[User]
Hey, I'm looking to get a base designed in OpenSCAD to hold some socket pieces I already have. I have 8 of them, but it'd be great if the design was parametric, so I could easily adjust the number of sockets. These sockets are based on a design I found online (thingiverse 1215001), and their base is 21mm wide.

I've included my attempt at the design as a starting point in `socketBase.scad`, but it needs a lot of work and I think it would be better to start from scratch.

Here's a breakdown of the base I'm looking for:

*   **Overall Structure**: It should be a solid block with cutouts and tracks along its sides.
*   **Socket Placement**: The sockets should be evenly spaced. Each "socket" is essentially a space designed to hold a cylindrical part 21mm wide, but the base should extend past the sides of these parts for structural integrity.
*   **Main Block Dimensions**: The main body should have a width that's 20mm wider than the base size (so 21 + 20 = 41mm width) and length that's 15mm longer than the total length of all the sockets together. If there are *n* sockets, that will be (21+1)*n+15 for the total length. This creates the bulk of the base.
*   **Inner Cutouts**: There should be a series of inner cutouts designed to make it a little lighter. A central rectangular cutout that runs the length of the base is helpful. It should be 4mm tall and centered vertically; inside of it, there should be another slightly taller (8mm) and narrower cutout, also centered.
*   **Outer Cutouts**: Along the sides, there should be a few simple rectangular cutouts along the top edges.
*    **Side Tracks**: There should also be tracks along both sides of the base. These are fairly complex shapes, and will need to be generated with a polyhedron.

I’m hoping you can make this parametric, so I can easily change the number of sockets (and have the base length adjust automatically). I'm not asking for individual socket pieces, just the base to hold them. I don’t think any external libraries are necessary for this, I was able to nearly generate the entire model with native SCAD functions.

[Assistant]
socketBase.scad:
```
/* [Base] */

NUMBERSOCKETS = 8;

/* [Hidden] */
BASE=21;

case();

module case()
{
 obWidth = BASE+20;
 obLength = (BASE+1)*NUMBERSOCKETS+15;

 difference()
  {
    difference()
    {
      outerBlock(obWidth, obLength);
      union()
      {
       translate([0,BASE/2-5,-1])
        cube([BASE+10, (BASE)*NUMBERSOCKETS+15, 4], center=true);
       translate([0,BASE/2-5,1])
        cube([BASE+2, (BASE)*NUMBERSOCKETS+15, 8], center=true);
      }
    }
    cuts(obWidth, obLength);
  }
       tracks(obWidth, obLength);
}

module outerBlock(obWidth, obLength)
{
    union()
    {
      cube([obWidth, obLength, 10], center=true);
      translate([0,6,-4.5])
       cube([obWidth, obLength+12,3], center=true);
      translate([0,obLength/2+5,-3])
       rotate([0,90,0])
        cylinder(r=3,h=obWidth/3, $fn=50, center=true);
    }
}

module cuts(obWidth, obLength)
{
   union()
   {
    translate([-obWidth/4,0,-7])
     cube([1,obLength/2,4]);
    translate([obWidth/4,0,-7])
     cube([1,obLength/2,4]);
    translate([-obWidth/2,obLength/2,-6])
     cube([obWidth/4+1,1,3]);
    translate([obWidth/4,obLength/2,-6])
     cube([obWidth/4,1,3]);
   }
}

module tracks(obWidth, obLength)
{
    union()
    {
        track(obWidth, obLength);
        mirror([1,0,0]) track(obWidth, obLength);
    }
}

module track(obWidth, obLength)
{
 translate([-obWidth/4-5.25,-obLength/2,-3.0])
 polyhedron(
    points=[ [0,0,0],[0,0,4],[4,0,4],
           [0,obLength,0],[0,obLength,4],[4,obLength,4]  ],
    faces=[ [0,1,2],[4,1,0,3],[1,4,5,2],[0,2,5,3],
              [5,4,3] ]
 );

}
```


[User]
Hi, I'm looking for some help designing an OpenSCAD model. I want to create a 3D printable object that's essentially a flat, dithered stereographic projection of an image. I've already got some basic parameters in mind that I need to be adjustable.

Here’s the basic idea: I want a circular base with an outer and inner radius, and a flat bottom created by a cutoff parameter (which is a percentage). This flat bottom will make it easier to print. The image is projected onto a sphere, which then gets "flattened" onto the flat base by cutting off the top of the sphere at the specified percentage.

The core feature I need is the halftoning or dithering. The input will be a grayscale image represented as a 1D array of values between 0 and 1, and I want the resulting shape to have holes that correspond to the lightness of the pixels, using a stereographic projection. Darker pixels should have smaller holes (or be solid), and lighter pixels will have larger holes. I need a few thresholds and min/max values to control the hole size: `black_threshold` controls at what point a pixel becomes solid black (no hole), `white_threshold` is for making a pixel completely transparent, `black_min` sets the minimum hole size for the darkest (non-solid) pixels, and `white_max` sets the maximum hole size for the lightest pixels. Pixels between the black and white threshold should have a hole based on their gray value and scaled by the black_min and white_max values.

I need to use the `build_plate.scad` library to easily define the printer's build plate area, so please make sure that's included. I want the basic build plate selector and manual x/y dimensions.

So, to recap, I need adjustable parameters for:

*   `outer_radius`:  The outer radius of the base circle.
*   `inner_radius`: The inner radius of the base circle.
*   `cutoff`: The percentage height of the sphere used for the flat base.
*   `pcutoff`: The cutoff value / 100 (I use this within my current implementation)
*   `white_threshold`:  Threshold for making pixels transparent.
*   `black_threshold`:  Threshold for making pixels solid.
*   `white_max`:  Maximum hole size.
*   `black_min`:  Minimum hole size.
*   `image`: The image array, which will be a 1D array of grayscale pixel values.
*   `build_plate_selector`: To select a preset build plate.
*   `build_plate_manual_x`: The manual x size of the build plate.
*   `build_plate_manual_y`: The manual y size of the build plate.

The pixel size should be determined automatically based on the square root of the length of the image array.

I'm hoping to get back a working OpenSCAD file (or files) that I can use right away!

[Assistant]
stereographic_flat_dither.scad:
```
include <utils/build_plate.scad>
// preview[view:south west, tilt:top diagonal]

outer_radius=50; //[20:200]
inner_radius=45; //[19:199]
cutoff=70; //[30:100]
pcutoff=cutoff/100;
white_threshold=1; //[0.5:0.01:1]
black_threshold=0.01; //[0:0.01:0.5]
white_max=0.95; //[0.5:0.01:1]
black_min=0.05; //[0:0.01:0.5]

build_plate_selector = 0; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]
build_plate_manual_x = 100; //[100:400]
build_plate_manual_y = 100; //[100:400]
build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);

image=[0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255,0.5019607843137255]; // [image_array:75x75]

size=sqrt(len(image));
hsize=size/2;
qsize=size/4;
northpole=[0,0,outer_radius]*1;

translate([0,0,outer_radius*cutoff/100]) {
    data=[for(x=[0:size-1]) let(
        rowdata=[for(y=[0:size-1]) (isPointVisible(x,y))?drawPoint(x,y,getPoint(x,y)):[[],[]]])
        addAllPointsAndFaces(rowdata,len(rowdata)-1)];
    polydata=addAllPointsAndFaces(data,len(data)-1);
    polyhedron(points=polydata[0],faces=polydata[1]);
}

function drawPoint(x,y,v)=let(
	xt=x-hsize,
	yt=y-hsize,
	p1=project((xt-0.5)/qsize,(yt-0.5)/qsize),
	p2=project((xt-0.5)/qsize,(yt+0.5)/qsize),
	p3=project((xt+0.5)/qsize,(yt+0.5)/qsize),
	p4=project((xt+0.5)/qsize,(yt-0.5)/qsize),
	f1=flatten(p1),
	f2=flatten(p2),
	f3=flatten(p3),
	f4=flatten(p4),
	p1i=intersectLineAndSphereAtCenter(northpole,p1*inner_radius,inner_radius*f1),
	p2i=intersectLineAndSphereAtCenter(northpole,p2*inner_radius,inner_radius*f2),
	p3i=intersectLineAndSphereAtCenter(northpole,p3*inner_radius,inner_radius*f3),
	p4i=intersectLineAndSphereAtCenter(northpole,p4*inner_radius,inner_radius*f4),
    p1o=intersectLineAndSphereAtCenter(northpole,p1i,outer_radius*f1),
    p2o=intersectLineAndSphereAtCenter(northpole,p2i,outer_radius*f2),
    p3o=intersectLineAndSphereAtCenter(northpole,p3i,outer_radius*f3),
    p4o=intersectLineAndSphereAtCenter(northpole,p4i,outer_radius*f4),
    pixel=(v<=black_threshold)?
        [[p1i,p2i,p3i,p4i,p1o,p2o,p3o,p4o],[[0,1,2],[0,2,3],[4,7,5],[7,6,5]]]
    :drawPointWithHole(xt,yt,v,p1i,p2i,p3i,p4i,p1o,p2o,p3o,p4o),
	s1=(x==0||!isPointVisible(x-1,y))?[[p1i,p1o,p2i,p2o],[[0,1,2],[1,3,2]]]:[[],[]],
	s2=(x==size-1||!isPointVisible(x+1,y))?[[p3i,p3o,p4i,p4o],[[0,1,2],[1,3,2]]]:[[],[]],
	s3=(y==0||!isPointVisible(x,y-1))?[[p4i,p4o,p1i,p1o],[[0,1,2],[1,3,2]]]:[[],[]],
	s4=(y==size-1||!isPointVisible(x,y+1))?[[p2i,p2o,p3i,p3o],[[0,1,2],[1,3,2]]]:[[],[]])
    addPointsAndFaces(s4,addPointsAndFaces(
        s3,addPointsAndFaces(addPointsAndFaces(pixel,s1),s2)));

function drawPointWithHole(xt,yt,v,p1i,p2i,p3i,p4i,p1o,p2o,p3o,p4o)=let(
        vl=black_min+v*(white_max-black_min),
        p5=project((xt-0.5*vl)/qsize,(yt-0.5*vl)/qsize),
        p6=project((xt-0.5*vl)/qsize,(yt+0.5*vl)/qsize),
        p7=project((xt+0.5*vl)/qsize,(yt+0.5*vl)/qsize),
        p8=project((xt+0.5*vl)/qsize,(yt-0.5*vl)/qsize),
        f5=flatten(p5),
        f6=flatten(p6),
        f7=flatten(p7),
        f8=flatten(p8),
        p5i=intersectLineAndSphereAtCenter(northpole,p5*inner_radius,inner_radius*f5),
        p6i=intersectLineAndSphereAtCenter(northpole,p6*inner_radius,inner_radius*f6),
        p7i=intersectLineAndSphereAtCenter(northpole,p7*inner_radius,inner_radius*f7),
        p8i=intersectLineAndSphereAtCenter(northpole,p8*inner_radius,inner_radius*f8),
    	p5o=intersectLineAndSphereAtCenter(northpole,p5i,outer_radius*f5),
        p6o=intersectLineAndSphereAtCenter(northpole,p6i,outer_radius*f6),
        p7o=intersectLineAndSphereAtCenter(northpole,p7i,outer_radius*f7),
        p8o=intersectLineAndSphereAtCenter(northpole,p8i,outer_radius*f8)
        )
        [[p1i,p2i,p3i,p4i,p5i,p6i,p7i,p8i,p1o,p2o,p3o,p4o,p5o,p6o,p7o,p8o],
		[[0,1,4],[1,5,4],[1,2,5],[2,6,5],[2,3,6],[3,7,6],[3,0,7],[0,4,7],
            [9,8,12],[13,9,12],[10,9,13],[14,10,13],[11,10,14],[15,11,14],[8,11,15],[12,8,15],
            [4,5,12],[5,13,12],[5,6,13],[6,14,13],[6,7,14],[7,15,14],[7,4,12],[7,12,15]]];

function addAllPointsAndFaces(data,i)=(i==0)?data[0]:addPointsAndFaces(addAllPointsAndFaces(data,i-1),data[i]);

function addPointsAndFaces(old,new)=[concat(old[0],new[0]),addFaces(old[1],new[1],len(old[0]))];

function addFaces(oldFaces,newFaces,pointNum)=concat(oldFaces,addToVectors(newFaces,pointNum));

function addToVectors(vec,x)=[for(v=vec) [for(a=v) a+x]];

function project(x,y)=[2*x/(1+x*x+y*y),2*y/(1+x*x+y*y),(x*x+y*y-1)/(1+x*x+y*y)];

function flatten(p)=(p[2]<-pcutoff)?1.02+pcutoff+p[2]:1;

function getPoint(x,y)=sqrt(image[y*size+x]);

function isPointVisible(x,y)=getPoint(x,y)<=white_threshold;

function lengthpow2(vec)=scalarProduct(vec,vec);

function scalarProduct(vec1,vec2)=vec1[0]*vec2[0]+vec1[1]*vec2[1]+vec1[2]*vec2[2];

function intersectLineAndSphereAtCenter(lp1, lp2, radius)=lp1+solveQuadraticEquationPositive(
	lengthpow2(lp2-lp1),
	2*scalarProduct(lp2-lp1,lp1),
	lengthpow2(lp1)-radius*radius)*(lp2-lp1);

function solveQuadraticEquationPositive(a,b,c)=(sqrt(b*b-4*a*c)-b)/(2*a);

```


[User]
Hey, I'm looking to get some custom scupper plugs designed for my Pelican kayak, and I think OpenSCAD is the way to go. I'm envisioning a parametric design so I can tweak things easily. I've seen some existing designs, but they're a bit limiting and not as customizable as I would like.

The basic idea is a plug that fits into the scupper holes, with options for different top and bottom styles. I'd need to define the plug length and diameters for both the top and bottom. The top end should have options for a bolt (with threads), a nut (with threads), a hook, a flat surface, and a pole holder, while the bottom should have options for a slot, a bolt, a nut, a hook, a GoPro mount, a flat surface, and a scoop.

For the threaded versions (both bolt and nut on either end), I need to be able to adjust the thread length, diameter, and pitch. I'd also want to control the level of detail used to render the thread and plug bodies. Additionally, I'm envisioning the ability to create a bevelled edge at the base of either the top or the bottom of the plug.

The slot on the bottom should also be parametrically adjustable so I can change the slot size. The pole holder option should have parameters for its length, outer and inner diameters, and the diameter of its base. I'll need a few experimental GoPro mount options too - a double connector, a triple connector, and a triple connector with a locking nut recess. Those would also need a parameter to control the base height.

I'm not very experienced with OpenSCAD, so I'm hoping you can provide a fairly complete solution for all of this. I'm okay with it being in separate files if needed for organization. I would like it to be parametric and I also hope it could be compatible with the standard libraries (like the one used to generate screw threads) that most users of OpenSCAD are familiar with, if possible.

I would also like a high degree of detail on the threads. Would it be possible to add a parameter for the level of detail that the thread uses?

[Assistant]
Custom_Threaded_Scupper_Plug-v1.7.scad:
```
//////////////////////////////////////////////////////////////////////
// Project: Custom Threaded scupper plugs for Pelican kayak         //
// Purpose: Stop me from getting a wet butt.                        //
// Add attachements in the future.                                  //
// Author: infinigrove Squirrel Master                              //
// Date: 23 December 2015    v1.3                                   //
//////////////////////////////////////////////////////////////////////

// Printing:
// Set to 10% to 20% infill with minimal material so they float!

// ---------------------------------------------------------------- //
// ------------------ BEGIN USER SERVICABLE CODE ------------------ //
// ---------------------------------------------------------------- //

//   !!!! NOTE: DIMENSIONS ARE METRIC AND IN MILIMETERS !!!!   //

// Length of scupper plug
plug_length = 32;

plug_detail = 40; //[40:360]

thread_detail = 2; //[2:8]

// Diameter of the top of plug
top_diameter = 26;

top_style = "bolt"; //[bolt,nut,hook,flat,pole_holder]

top_base_bevel = "no"; //[yes,no]

top_thread_length = 15;
top_thread_diameter = 22;
top_thread_pitch = 4; //[2:6]

// Diameter of bottom of plug
bottom_diameter = 38;

// Which end would you like? (bottom)
bottom_style = "slot"; //[slot,bolt,nut,hook,gopro,flat,scoop]

bottom_base_bevel = "no"; //[yes,no]

/* [bottom slot] */

slot_size = 2; // [1:10]

/* [bottom bolt nut] */

bottom_thread_length = 47;
bottom_thread_diameter = 18;
bottom_thread_pitch = 4; //[2:6]

/* [Top Pole Holder EXPERIMENTAL] */

pole_holder_length = 115;
pole_holder_outer_diameter = 40;
pole_holder_inner_diameter = 28;
pole_holder_base_diameter = 80;

/* [GoPro Connector EXPERIMENTAL] */

gopro_style="triple_nut"; // [triple_nut,triple,double]
gopro_base_height = 48;

/* [Hidden] */

// The gopro connector itself (you most probably do not want to change this but for the first two)

// The locking nut on the gopro mount triple arm mount (keep it tight)
gopro_nut_d= 9.2;
// How deep is this nut embossing (keep it small to avoid over-overhangs)
gopro_nut_h= 2;
// Hole diameter for the two-arm mount part
gopro_holed_two= 5;
// Hole diameter for the three-arm mount part
gopro_holed_three= 5.5;
// Thickness of the internal arm in the 3-arm mount part
gopro_connector_th3_middle= 3.1;
// Thickness of the side arms in the 3-arm mount part
gopro_connector_th3_side= 2.7;
// Thickness of the arms in the 2-arm mount part
gopro_connector_th2= 3.04;
// The gap in the 3-arm mount part for the two-arm
gopro_connector_gap= 3.1;
// How round are the 2 and 3-arm parts
gopro_connector_roundness= 1;
// How thick are the mount walls
gopro_wall_th= 3;

gopro_connector_wall_tol=1.1+0;
gopro_tol=0.04+0;

// Can be queried from the outside
gopro_connector_z= 2*gopro_connector_th3_side+gopro_connector_th3_middle+2*gopro_connector_gap;
gopro_connector_x= gopro_connector_z;
gopro_connector_y= gopro_connector_z/2+gopro_wall_th;
// ---------------------------------------------------------------- //
// ------------------- END USER SERVICABLE CODE ------------------- //
module plug_torus(r,rnd)
{
	translate([0,0,rnd/2])
		rotate_extrude(convexity= 10, $fn=plug_detail)
			translate([r-rnd/2, 0, 0])
				circle(r= rnd/2, $fs=0.2);
}
// ---------------------------------------------------------------- //
// --- screw thread modules --------------------------------------- //
/*    Minimal thread modules hacked from
 *    polyScrewThread.scad    by aubenc @ Thingiverse
 *
 * This script contains the library modules that can be used to
 * generate screw and nut threads.
 *
 * http://www.thingiverse.com/thing:8796
 *
 * CC Public Domain
 */
// --- modules added for use with thingverse cusomizer ------------ //
// ---------------------------------------------------------------- //
module screw_thread(od,st,lf0,lt,rs,cs)
{
    or=od/2;
    ir=or-st/2*cos(lf0)/sin(lf0);
    pf=2*PI*or;
    sn=floor(pf/rs);
    lfxy=360/sn;
    ttn=round(lt/st)+1;
    zt=st/sn;

    intersection()
    {
        if (cs >= -1)
        {
           # thread_shape(cs,lt,or,ir,sn,st);
        }

        full_thread(ttn,st,sn,zt,lfxy,or,ir);
    }
}

module thread_shape(cs,lt,or,ir,sn,st)
{
    if ( cs == 0 )
    {
        cylinder(h=lt, r=or, $fn=sn, center=false);
    }
    else
    {
        union()
        {
            translate([0,0,st/2])
              cylinder(h=lt-st+0.005, r=or, $fn=sn, center=false);

            if ( cs == -1 || cs == 2 )
            {
                cylinder(h=st/2, r1=ir, r2=or, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }

            translate([0,0,lt-st/2])
            if ( cs == 1 || cs == 2 )
            {
                  cylinder(h=st/2, r1=or, r2=ir, $fn=sn, center=false);
            }
            else
            {
                cylinder(h=st/2, r=or, $fn=sn, center=false);
            }
        }
    }
}

module full_thread(ttn,st,sn,zt,lfxy,or,ir)
{
    for(i=[0:ttn-1])
    {
        for(j=[0:sn-1])
        {
            polyhedron(
                  points=[
                          [0,                  0,                  i*st-st            ],
                          [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt-st   ],
                          [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt-st       ],
                          [or*cos((j+1)*lfxy), or*sin((j+1)*lfxy), i*st+(j+1)*zt-st/2 ],
                          [or*cos(j*lfxy),     or*sin(j*lfxy),     i*st+j*zt-st/2     ],
                          [0,                  0,                  i*st+st            ],
                          [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt      ],
                          [ir*cos(j*lfxy),     ir*sin(j*lfxy),     i*st+j*zt          ]
                         ],
               triangles=[
                          [0,1,2],
                          [5,6,3],[5,3,0],[0,3,1],
                          [3,4,1],[1,4,2],
                          [3,6,4],[4,6,7],
                          [0,2,4],[0,4,5],[5,4,7],
                          [5,7,6]
                         ],
               convexity=5);
        }
    }
}
//GoPro connector module hacked from
// gopro_mounts_mooncactus.scad by MoonCactus
// http://www.thingiverse.com/thing:62800
module gopro_torus(r,rnd)
{
	translate([0,0,rnd/2])
		rotate_extrude(convexity= 10, $fn=plug_detail/2)
			translate([r-rnd/2, 0, 0])
				circle(r= rnd/2, $fs=0.2);
}

module gopro_connector(version="double", withnut=true, captive_nut_th=0, captive_nut_od=0, captive_rod_id=0, captive_nut_angle=0)
{
	module gopro_profile(th)
	{
		hull()
		{
			gopro_torus(r=gopro_connector_z/2, rnd=gopro_connector_roundness);
			translate([0,0,th-gopro_connector_roundness])
				gopro_torus(r=gopro_connector_z/2, rnd=gopro_connector_roundness);
			translate([-gopro_connector_z/2,gopro_connector_z/2,0])
				cube([gopro_connector_z,gopro_wall_th,th]);
		}
	}
	difference()
	{
		union()
		{
			if(version=="double")
			{
				for(mz=[-1:2:+1]) scale([1,1,mz])
						translate([0,0,gopro_connector_th3_middle/2 + (gopro_connector_gap-gopro_connector_th2)/2]) gopro_profile(gopro_connector_th2);
			}
			if(version=="triple")
			{
				translate([0,0,-gopro_connector_th3_middle/2]) gopro_profile(gopro_connector_th3_middle);
				for(mz=[-1:2:+1]) scale([1,1,mz])
					translate([0,0,gopro_connector_th3_middle/2 + gopro_connector_gap]) gopro_profile(gopro_connector_th3_side);
			}

			// add the common wall
			//translate([0,gopro_connector_z/2+gopro_wall_th/2+gopro_connector_wall_tol,0])
				//cube([gopro_connector_z,gopro_wall_th,gopro_connector_z], center=true);
            translate([0,(gopro_connector_z/2+gopro_wall_th/2+gopro_connector_wall_tol)+gopro_base_height,0]) rotate([90,0,0]) cylinder(h=gopro_base_height,d1=bottom_diameter,d2=gopro_connector_z*1.5,$fn=plug_detail);

			// add the optional nut emboss
			if(version=="triple" && withnut)
			{
				translate([0,0,gopro_connector_z/2-gopro_tol])
				difference()
				{
					cylinder(r1=gopro_connector_z/2-gopro_connector_roundness/2, r2=11.5/2, h=gopro_nut_h+gopro_tol, $fn=plug_detail /2);
					cylinder(r=gopro_nut_d/2, h=gopro_connector_z/2+3.5+gopro_tol, $fn=6);
				}
			}
		}
		// remove the axis
		translate([0,0,-gopro_tol])
			cylinder(r=(version=="double" ? gopro_holed_two : gopro_holed_three)/2, h=gopro_connector_z+4*gopro_tol, center=true, $fn=plug_detail /2);
	}
}

// ---------------------------------------------------------------- //
// --- render a custom threaded scupper plug ---------------------- //
// ---             MAIN CODE                 ---------------------- //
// ---------------------------------------------------------------- //
/*    Partially hacked and major additions from
 *    Glide_Kayak_Scuppers_-_Mahi_rev2.scad    by Max Bainrot
 *
 * http://www.thingiverse.com/thing:951903
 *
 */
// ---------------------------------------------------------------- //
// ------- can be thanked for hook scupper end -------------------- //
// ---------------------------------------------------------------- //
PI=3.141592;
translate([0,0,plug_length])
rotate([0,180,0])
difference()
{
    union()
    {
        cylinder(h=(plug_length), r1=(top_diameter/2), r2=(bottom_diameter /2),$fn=plug_detail);

        // GoPro connector
        if (bottom_style == "gopro")
        {
            if (gopro_style == "double") translate([0,0,plug_length+10+gopro_base_height])  rotate([270,0,0]) gopro_connector("double");
            if (gopro_style == "triple") translate([0,0,plug_length+10+gopro_base_height])  rotate([270,0,0]) gopro_connector("triple", withnut=false);
            if (gopro_style == "triple_nut") translate([0,0,plug_length+10+gopro_base_height])  rotate([270,0,0]) gopro_connector("triple", withnut=true);
        }
        // scoop connector
        if (bottom_style == "scoop") translate([0,0,plug_length]) sphere(d=bottom_diameter, $fn=plug_detail);;

        // pole holder base
        if (top_style == "pole_holder") translate([0,0,-(pole_holder_outer_diameter /3)]) cylinder( h=pole_holder_outer_diameter /3,   d1=pole_holder_outer_diameter,    d2=pole_holder_base_diameter, $fn=plug_detail);

        if (top_style == "bolt") translate([0,0,-(top_thread_length)]) screw_thread(top_thread_diameter,top_thread_pitch,55,top_thread_length,PI/thread_detail,2);

        if (bottom_style == "bolt") translate([0,0,plug_length]) screw_thread(bottom_thread_diameter,bottom_thread_pitch,55,bottom_thread_length,PI/thread_detail,2);

        if (top_style == "hook") translate([0,(top_diameter /8),0]) rotate([90,0,0]) cylinder(d=top_diameter - 3,h=top_diameter /4,$fn=plug_detail);

        if (bottom_style == "hook") translate([0,(bottom_diameter /8),plug_length]) rotate([90,0,0]) cylinder(d=bottom_diameter - 3,h=bottom_diameter /4,$fn=plug_detail);

        if (bottom_base_bevel == "yes") translate([0,0,plug_length-(gopro_connector_roundness/1.4)]) plug_torus(r=bottom_diameter/2, rnd=gopro_connector_roundness);

        if (top_base_bevel == "yes") translate([0,0,-(gopro_connector_roundness/1.4)]) plug_torus(r=top_diameter/2, rnd=gopro_connector_roundness);

    }
    if (top_style == "hook") translate([0,0,0]) sphere(d=top_diameter /1.5, $fn=plug_detail);

    if (bottom_style == "hook") translate([0,0,plug_length]) sphere(d=bottom_diameter /1.5, $fn=plug_detail);

    if (top_style == "nut") screw_thread(top_thread_diameter,top_thread_pitch,55,top_thread_length,PI/thread_detail,0);

    if (bottom_style == "nut") translate([0,0,(plug_length - bottom_thread_length)]) screw_thread(bottom_thread_diameter,bottom_thread_pitch,55,bottom_thread_length,PI/thread_detail,0);

    if (bottom_style == "slot") translate([-(bottom_diameter /2),-(slot_size /2),(plug_length - slot_size)]) cube([bottom_diameter,slot_size,slot_size+gopro_connector_roundness]);

    if (bottom_style == "scoop") translate([0,0,plug_length])
    {
        //remove half with block and scoop out whats left
        translate([-(bottom_diameter /2), 0, 0])cube([bottom_diameter,bottom_diameter,bottom_diameter]);
        sphere(d=bottom_diameter - 4, $fn=plug_detail);
        //remove cylinder in plug
        translate([0,0,-plug_length]) cylinder(h=plug_length, d1=(top_diameter - 4), d2=(bottom_diameter - 4), $fn=plug_detail);

        //remove cylinder in top_bolt
        translate([0,0,-(plug_length + top_thread_length)]) cylinder(h=top_thread_length, d1=(top_thread_diameter - top_thread_pitch), d2=(top_thread_diameter - top_thread_pitch), $fn=plug_detail);

        //translate([0,0,-(plug_length + top_thread_length)]) cylinder(h=(plug_length + top_thread_length), d1=(bottom_thread_diameter - bottom_thread_pitch), d2=(bottom_diameter - 4), $fn=plug_detail);

    }
}

// pole holder main cylinder
if (top_style == "pole_holder")
{
    // you may need to tweek this traslate depending on size of pole holder
    translate([(pole_holder_outer_diameter/1.1),0,(plug_length + pole_holder_length)*.98])
    rotate([0,200,0])
    difference()
    {
        union()
        {
            // pole holder outer
            cylinder(h=pole_holder_length, d=pole_holder_outer_diameter,$fn=plug_detail);

        }

        // pole holder inner
        cylinder(h=(pole_holder_length - (pole_holder_outer_diameter - pole_holder_inner_diameter)), d=pole_holder_inner_diameter,$fn=plug_detail);
    }
}

```


[User]
Okay, I'd like a parametric OpenSCAD design for a button. I want to be able to choose from several shapes, specifically round, square, a flower, a cat head, a bunny head, a Mickey Mouse-like shape with two offset circles for ears, and a heart shape.

I need to control the overall diameter or length of the button, its thickness, whether or not there's a raised border around the edge, the thickness and size of that border, the number of holes for sewing (2, 4, or 6), the diameter of the holes, and the diameter of the circle the holes are arranged on. I need to be able to choose if there is a border, and to set the thickness of it, and the distance from the edge of the button to the inner edge of the border.

I'd prefer that all of this be contained in a single .scad file for ease of use. I don't need any external libraries.

[Assistant]
button.scad:
```
//+-------------------------------------------------+
//|OPENSCAD Bouton de mercercie - haberdasher Button|
//|        2015 gaziel@gmail.com                    |
//+- -----------------------------------------------+

//your choice...
type="love";//[round,square,flower,cat,bunny,MMice,love]

//external diameter or length of the button
bd=10;

//thinkness
bep=1;

//border ? Yes of course!
border="yes";//[yes,no]

//allowance
borderep=1;

//size  for the border
borderd=1;

//number of holes
hn=2;//[2,4,6]

//hole diameter
hd=1;

//holes repartition diameter
hr=4;

$fn=100/2;

bouton();

module bouton(){
    difference(){
        corps();
        for (i=[1:hn]){
            rotate([0,0,(i*360)/hn])translate([(hr+hd)/2,0,0]) cylinder(d=hd,h=bep+1,center=true);
        }
    }
}
module corps(){
   if (type=="round"){
       cylinder(d=bd,h=bep,center=true);
       if (border=="yes"){
           difference(){
               translate([0,0,bep/2])cylinder(d=bd,h=borderep,center=true);
               translate([0,0,bep/2])cylinder(d=bd-borderd,h=borderep+1,center=true);
           }
        }
   }
  if (type=="square"){
   cube([bd,bd,bep],center=true);
   if (border=="yes"){
       difference(){
           translate([0,0,bep/2]) cube([bd,bd,borderep],center=true);
           translate([0,0,bep/2])cube([bd-borderd,bd-borderd,borderep+1],center=true);
       }
    }
}
  if (type=="flower"){
   for (i=[1:6]){
    rotate([0,0,(i*360)/6])translate([bd/3,0,0]) cylinder(d=bd/3,h=bep,center=true);
   }
   cylinder(d=bd/1.6,h=bep,center=true);
   if (border=="yes"){
       difference(){
           union(){
                  for (i=[1:6]){
translate([0,0,bep/2])    rotate([0,0,(i*360)/6])translate([bd/3,0,0]) cylinder(d=bd/3,h=borderep,center=true);
   }
  translate([0,0,bep/2]) cylinder(d=bd/1.6,h=borderep,center=true);
           }
                      union(){
                  for (i=[1:6]){
translate([0,0,bep/2])    rotate([0,0,(i*360)/6])translate([bd/3,0,0]) cylinder(d=(bd/3)-borderd,h=borderep+1,center=true);
   }
  translate([0,0,bep/2]) cylinder(d=(bd/1.6)-borderd/2,h=borderep+1,center=true);
           }

       }
    }
}
    if (type=="cat"){
        cylinder(d=bd,h=bep,center=true);
        difference(){
            union(){
                rotate([0,0,33])scale([0.5,1.5,1])cylinder(d=bd,h=bep,center=true);
                rotate([0,0,-33])scale([0.5,1.5,1])cylinder(d=bd,h=bep,center=true);
            }
            translate([-bd/2,0,-bd/2])cube(bd);

        }
       if (border=="yes"){
           difference(){
               translate([0,0,bep/2])cylinder(d=bd,h=borderep,center=true);
               translate([0,0,bep/2])cylinder(d=bd-borderd,h=borderep+1,center=true);
           }
        }
   }

       if (type=="bunny"){//bof
        cylinder(d=bd,h=bep,center=true);
        difference(){
            union(){
                translate([bd/2,-bd/2,0])rotate([0,0,33])scale([0.5,1.2,1])cylinder(d=bd,h=bep,center=true);
                translate([-bd/2,-bd/2,0])rotate([0,0,-33])scale([0.5,1.2,1])cylinder(d=bd,h=bep,center=true);
            }

        }
       if (border=="yes"){
           difference(){
               translate([0,0,bep/2])cylinder(d=bd,h=borderep,center=true);
               translate([0,0,bep/2])cylinder(d=bd-borderd,h=borderep+1,center=true);
           }
        }
   }

      if (type=="MMice"){
        cylinder(d=bd,h=bep,center=true);
        difference(){
            union(){
                translate([bd/2,-bd/2,0])rotate([0,0,33])cylinder(d=bd/1.618,h=bep,center=true);
                translate([-bd/2,-bd/2,0])rotate([0,0,-33])cylinder(d=bd/1.618,h=bep,center=true);
            }
         }
       if (border=="yes"){
           difference(){
               translate([0,0,bep/2])cylinder(d=bd,h=borderep,center=true);
               translate([0,0,bep/2])cylinder(d=bd-borderd,h=borderep+1,center=true);
           }
        }
   }
     if (type=="love"){
       translate([0,-bd/8,0])rotate([0,0,45]){
         cube([bd,bd,bep],center=true);
         translate([bd/2,0,0])cylinder(d=bd,h=bep,center=true);
         translate([0,bd/2,0])cylinder(d=bd,h=bep,center=true);
       }
       if (border=="yes"){
           difference(){
               translate([0,0,bep/2]) union(){
                 translate([0,-bd/8,0])rotate([0,0,45]){
                 cube([bd,bd,bep],center=true);
                 translate([bd/2,0,0])cylinder(d=bd,h=bep,center=true);
                 translate([0,bd/2,0])cylinder(d=bd,h=bep,center=true);
                 }
               }
               translate([0,0,bep/2]) union(){
                 translate([0,-bd/8,0])rotate([0,0,45]){
                 cube([bd-borderd,bd-borderd,bep+1],center=true);
                 translate([bd/2,0,0])cylinder(d=bd-borderd,h=bep+1,center=true);
                 translate([0,bd/2,0])cylinder(d=bd-borderd,h=bep+1,center=true);
                 }
               }
            }
       }
     }
 }

```