[User]
I'd like to create a metaball generator in OpenSCAD, similar to the provided `SomeBalls.scad` code, but with more flexibility and control. I'm not going to provide the code as an example, but the basic principle is that of the "marching cubes" algorithm. I'd like to define a list of spheres using `[[x, y, z, r], [x, y, z, r], ...]` format, with `x, y, z` representing the center, and `r` representing the radius of the sphere. The result should be a smooth, blobby object that represents the surface where the influence of those spheres combine.

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

1.  **Resolution:**  I need a parameter to control the resolution of the marching cubes grid, affecting the smoothness and detail of the final shape. Smaller resolutions should give smoother, blobby results, and larger resolutions more detailed and faceted.
2.  **Threshold:** A parameter to define the iso-surface threshold of the metaball.  This value is where the combined influence of the spheres is considered to be the surface. Larger thresholds should result in a smaller object.
3.  **Subdivision Control:**  Instead of a fixed number, I need parameters to define both the minimum subdivision level (like the `MinSubdivisions` in the example) and the maximum subdivision level. This would allow me to control the level of detail based on the resolution parameter. Perhaps allow the user to define the size that a cube must be before it stops subdividing.
4.  **Oversize:** I need a parameter to control the size of the bounding box surrounding the spheres that will be used to calculate the metaball surface (like `Oversize` in the example).
5.  **Coloring:**  I would like each sphere to be associated with a color that influences the color of the resulting metaball. This should be a smooth transition from one color to another. I want the color list to be the same length as the sphere list. Each color is a vector of 3, like `[r,g,b]`.
6.  **Parametric Spheres:**  I'd like the ability to easily define the list of spheres with random or generated values based on other parameters, like the number of spheres.

I would like the output to be a watertight solid. The `SomeBalls.scad` example code seems to be using a rather crude method of only testing the corners of a cube, and it could be improved quite a bit. It uses the 'hull' operation to join the resulting geometry, and that seems to cause some problems.

I'm open to using libraries if they would make this easier, but I'd like to avoid adding dependencies if possible.

[Assistant]
SomeBalls.scad:
```
/*
SomeBalls.scad
Public Domain Code
Based on Metaballs By: William A Adams 	4 June 2011

This code is a simplified implementation of the 'metaball' and uses "Marching Cubes" to register the isosurface which is the interaction of the various balls. All Cubes are tested and a joint hull is generated of each corner that lies inside the threshold  of the metaball

Future work :
For obvious reasons linearly interpolated marching cubes.
A more elegant cube solution than just hulling corners.
Cube subdivision for speed up.

*/
Resolution = 10;//[5:5:50]
MinSubdivisions=4;//[2:1:10]
// Smaller number larger blob
Threshold = 1.85;// [1.5:0.01:2.5]
Diagnostic=false;
Oversize=0.6;
// List Format [[x,y,z,r],[x,y,z,r]]
MyBallList=[[50, 50, 50, -11],[25, 5, 5, 35],[95, 5, 75, 45],[-35, 5, 75, 55]];//
//MyBallList=[ for(i=[0:5])Lotto()];
DrawMetaballs(MyBallList);

module DrawMetaballs(BallList) {
 ColorList = [ for(i=[0:len(BallList)])rndc() ];

// boundary plus some extra
size=max(findMax(BallList, 0),findMax(BallList, 1),findMax(BallList,2));
DrawSubMetaballs(BallList,ColorList,findMin(BallList, 0)-(size*Oversize),findMin(BallList, 1)-(size*Oversize),findMin(BallList, 2)-(size*Oversize),size*(Oversize*2+1));

 }
module DrawSubMetaballs(BallList,ColorList,x,y,z,Size,i=0) {
  //  echo(i,Size);

suma = BallField3D(x, y, z, BallList, len(BallList) - 1);
sumb = BallField3D(x + Size, y, z, BallList, len(BallList) - 1);
sumc = BallField3D(x, y + Size, z, BallList, len(BallList) - 1);
sumd = BallField3D(x + Size, y + Size, z, BallList, len(BallList) - 1);
sume = BallField3D(x, y, z + Size, BallList, len(BallList) - 1);
sumf = BallField3D(x + Size, y, z + Size, BallList, len(BallList) - 1);
sumg = BallField3D(x, y + Size, z + Size, BallList, len(BallList) - 1);
sumh = BallField3D(x + Size, y + Size, z + Size, BallList, len(BallList) - 1);
sumcolor =lim31(1, ColorField3D(x, y, z, BallList,ColorList, len(BallList) - 1));

sab=inter(suma,sumb)*Size;
sac=inter(suma,sumc)*Size;
sae=inter(suma,sume)*Size;
sba=inter(sumb,suma)*Size;
sbd=inter(sumb,sumd)*Size;
sbf=inter(sumb,sumf)*Size;
sca=inter(sumc,suma)*Size;
scd=inter(sumc,sumd)*Size;
scg=inter(sumc,sumg)*Size;
sdb=inter(sumd,sumb)*Size;
sdc=inter(sumd,sumc)*Size;
sdh=inter(sumd,sumh)*Size;
sea=inter(sume,suma)*Size;
seg=inter(sume,sumg)*Size;
sef=inter(sume,sumf)*Size;
sfh=inter(sumf,sumh)*Size;
sfe=inter(sumf,sume)*Size;
sfb=inter(sumf,sumb)*Size;
sge=inter(sumg,sume)*Size;
sgh=inter(sumg,sumh)*Size;
sgc=inter(sumg,sumc)*Size;
shg=inter(sumh,sumg)*Size;
shf=inter(sumh,sumf)*Size;
shd=inter(sumh,sumd)*Size;

// test for empty and full Cubes
 if (Size>pow(Resolution, MinSubdivisions)||i<MinSubdivisions) {
    h=Size*0.5  ;
    DrawSubMetaballs(BallList,ColorList,x,y,z,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y,z,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x,y+h,z,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y+h,z,h,i+1);

    DrawSubMetaballs(BallList,ColorList,x,y,z+h,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y,z+h,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x,y+h,z+h,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y+h,z+h,h,i+1);

    }else {

if (  suma <= Threshold && sumb <= Threshold && sumc <= Threshold && sumd <= Threshold && sume <= Threshold && sumf <= Threshold && sumg <= Threshold && sumh <= Threshold) {if (Diagnostic){translate([x, y, z]) %cube(Size);}}
else if ( suma > Threshold && sumb > Threshold && sumc > Threshold && sumd > Threshold && sume > Threshold && sumf > Threshold && sumg > Threshold && sumh > Threshold) {
translate([x, y, z]) cube(Size);}

else if(Size>Resolution){
    h=Size*0.5  ;
    DrawSubMetaballs(BallList,ColorList,x,y,z,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y,z,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x,y+h,z,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y+h,z,h,i+1);

    DrawSubMetaballs(BallList,ColorList,x,y,z+h,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y,z+h,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x,y+h,z+h,h,i+1);
    DrawSubMetaballs(BallList,ColorList,x+h,y+h,z+h,h,i+1);}
else {

    // For every corner inside treshold make and mirror a corner piece, hull the lot.
   //echo(sumcolor);
    color(sumcolor)hull(){
if (suma > Threshold){    translate([x, y, z])  Corner(sab,sac,sae);}
if (sumb > Threshold){    translate([x + Size, y, z])  Corner(-sba,sbd,sbf);}
if (sumc > Threshold){  translate([x, y + Size, z])  Corner(scd,-sca,scg);}
if (sumd > Threshold){    translate([x + Size, y + Size, z]) Corner(-sdc,-sdb,sdh);}

if (sume > Threshold){   translate([x, y, z + Size])  Corner(sef,seg,-sea);}
if (sumf > Threshold){   translate([x + Size, y, z + Size])  Corner(-sfe,sfh,-sfb);}
if (sumg > Threshold){    translate([x, y + Size, z + Size])  Corner(sgh,-sge,-sgc);}
if (sumh > Threshold){    translate([x + Size, y + Size, z + Size]) Corner(-shg,-shf,-shd);}
}
    }
}}

//Corner piece
module Corner(u=0.5,v=0.5,w=0.5) {scale([u,v,w])
polyhedron
    (points = [	       [0,0,0],[1,0,0],[0,1,0],[0,0,1],],      faces = [		  [0,0,1],[0,0,2],[0,0,3],[1,2,3]
		  ]
     );}

     function lim31(l, v) = v / len3(v) * l;
     function len3(v) = sqrt(pow(v[0], 2) + pow(v[1], 2) + pow(v[2], 2));
// Random color

function Lotto() = [rands(0, 200, 1)[0],rands(0, 100, 1)[0],rands(0, 100, 1)[0],rands(20, 21, 1)[0]];

//Field function
     function ColorField3D(x, y, z, BallSack,Colorsack, i) = (i == 0) ? Powercolor(x, y, z, BallSack[0])*Colorsack[0]: Powercolor(x, y, z, BallSack[i])*Colorsack[i] + ColorField3D(x, y, z, BallSack,Colorsack, i - 1);

function Powercolor(x, y, z, Ball) = (Ball[3] / sqrt((x - Ball[0]) * (x - Ball[0]) + (y - Ball[1]) * (y - Ball[1]) + (z - Ball[2]) * (z - Ball[2])));
function Powerball(x, y, z, Ball) = (Ball[3] / sqrt((x - Ball[0]) * (x - Ball[0]) + (y - Ball[1]) * (y - Ball[1]) + (z - Ball[2]) * (z - Ball[2])));
// Recursive Field Evaluation
function BallField3D(x, y, z, BallSack, i) = (i == 0) ? Powerball(x, y, z, BallSack[0]): Powerball(x, y, z, BallSack[i]) + BallField3D(x, y, z, BallSack, i - 1);
function rndc() = [round(rands(0, 2, 1)[0])/2, round(rands(0, 2, 1)[0])/2, round(rands(0, 2, 1)[0])/2];
     //interpolate
function inter(t,r)=(t==r)?1:(t>Threshold)?(r<Threshold)? abs(Threshold-t)/abs(t-r) :0 :(r>Threshold)? abs(Threshold-t)/abs(t-r) :1 ;
// Recursive find min/max extremes
function findMax(v, select, i = 0) = ((i + 1) < len(v)) ? max(v[i][select] + v[i][3] + Resolution, findMax(v, select, i + 1)): v[i][select] + v[i][3] + Resolution;
function findMin(v, select, i = 0) = ((i + 1) < len(v)) ? min(v[i][select] - v[i][3] - Resolution, findMin(v, select, i + 1)): v[i][select] - v[i][3] - Resolution;
```


[User]
I'd like to create a simple bookend design in OpenSCAD. I'm envisioning two mirrored "L" shapes that will stand up on their own.

I'd like to be able to adjust the length, width, and height of the bookends. Also, I need to specify the thickness of the material used, which would be different for the main body and for the backing. I'm thinking something along the lines of a 'wall thickness' and a 'back thickness' would be good names. Also, I want some tolerance so the cutouts don't press against the exterior walls.

The design should have a rectangular cutout in each of the "L" shapes, kind of like a window. The cutout should leave a frame around it, using the 'wall thickness'. The back is a simple rectangle, set in a little ways from the back of each shape.

Could you generate the necessary OpenSCAD code for this? I'm not using any external libraries.

[Assistant]
Books_stands_.scad:
```
$fn = 12;

//lenght
l = 100;
//width
w = 100;
//height
h = 100;

module toto() {
}

ep_bord = 2;
ep_fon = 0.5;

translate([l,0,0])
    right(l,w,h,0);

translate([0,w,0])
    rotate([0,0,180])
        left(l,w,h,0);

module right (length,width,height) {

    difference() {
        cube([length,width,height]);
        translate([ep_bord/2,0,ep_fon])
        cube([width+5,length-ep_bord+5,height - ep_fon]);
    }

}

module left (length,width,height) {

        difference() {
        cube([length,width,height]);
        translate([ep_bord/2,0,ep_fon])
        cube([width+5,length-ep_bord+5,height - ep_fon]);
    }

}

```


[User]
Hey, I'm looking to get an OpenSCAD design for a fan cover, specifically something that would protect a top-mounted fan from water spills. I need it to be parametric so I can adjust the dimensions for different fans.

The basic idea is a sort of shallow, truncated cone shape, with the wider end on top, and then mounting holes for screws. The design will be something like two concentric truncated cones stacked on top of each other, creating a 'lip' or a 'ledge' at the top. The screw holes should go through this ledge.

Specifically, I'd need the following parameters exposed:
*   `elevation`: The height of the cover above the fan mounting surface.
*   `height`: The overall height of the truncated cone (or the stacked cones, specifically the vertical distance the cones extend beyond the 'ledge').
*   `d1`: The diameter of the top opening of the truncated cone.
*  `d2`: The diameter of the base (where it will attach to the fan surface) of the truncated cone.
*   `thickness`: The thickness of the material.
*   `screwH`: The height of the screw cylinders.
*   `screwRi`: The radius of the screw holes (inner radius).
*   `screwRo`: The radius of the screw cylinder itself (outer radius).
*  `D`: Distance from the edge of the outer circle to the center of the screws.
The cover should have 4 screw holes, and they should be placed symmetrically around the edge (think one on each cardinal direction, 90 degrees apart).

I'd like the design to be based off of the BOSL2 library. Thanks!

[Assistant]
Fan_Cover_Top.scad:
```
$fn=100;
ly = .2;

elevation = 38;
height = 20;

d1 = 140;
d2 = d1-d1/3;

thickness = 4*ly;
screwH = height/1.5;
screwRi = 4.3/2;
screwRo = 1.5*screwRi;
D = (140-124.5-screwRi*2)/2;

S1 = [d1/2-screwRo-D,0,0];
S2 = [0,d1/2-screwRo-D,0];
S3 = [-d1/2+screwRo+D,0,0];
S4 = [0,-d1/2+screwRo+D,0];

    difference() {
        union() {
            translate([0,0,elevation]) {
                difference() {
                    translate([0,0,thickness]) cylinder(h=height,r1=d1/2,r2=d2/2,$fn=4);
                    cylinder(h=height,r1=d1/2,r2=d2/2,$fn=4);
                }
            }
            translate(S1) cylinder(h=elevation+screwH,r1=screwRo,r2=screwRo);
            translate(S2) cylinder(h=elevation+screwH,r1=screwRo,r2=screwRo);
            translate(S3) cylinder(h=elevation+screwH,r1=screwRo,r2=screwRo);
            translate(S4) cylinder(h=elevation+screwH,r1=screwRo,r2=screwRo);
        }
        translate(S1) cylinder(h=elevation+screwH,r1=screwRi,r2=screwRi);
        translate(S2) cylinder(h=elevation+screwH,r1=screwRi,r2=screwRi);
        translate(S3) cylinder(h=elevation+screwH,r1=screwRi,r2=screwRi);
        translate(S4) cylinder(h=elevation+screwH,r1=screwRi,r2=screwRi);
    }
```


[User]
Hey, I'm looking for some help designing a parametric maze generator in OpenSCAD. I really like the idea of having a maze on a gyro, with two circular maze patterns on either side of a central sphere-like hub, like a wheel with mazes on the faces.

I've got a few parameters in mind that I'd like to be able to control:

*   **`radius`**: This would set the overall radius of the gyro, and therefore the size of the sphere hub.
*   **`spacing`**: This would control the space between the different "rings" of the maze. I’d like to be able to make this a parameter to experiment with different ring overlaps based on printer settings.
*   **`wall_height`**: This would set the height of the maze walls.
*   **`wall_thickness`**: The thickness of the maze walls.
*   **`cblocks`**: This would control the number of "circular" blocks in the maze. Think of it as a number of wedges around the circumference of a circle.
*   **`rblocks`**: This would control the number of "radial" blocks; or number of rings/layers to the maze.
*   **`ring`**: A boolean yes/no parameter indicating whether to add a ring feature around the circumference of the gyro.

The maze itself should be generated using a recursive backtracker algorithm to guarantee a solvable maze. Each maze should be distinct and each generated with these parameters. I'd like the code to handle the generation of two independent but similar mazes, one on each side of the gyro.

I have the functions that map a set of radial and circular blocks to a maze, and I have the modules for generating the basic shapes.

I need the whole design with the following modules:
*   `a_quarter_arc`: Creates a quarter arc segment.
*   `arc`: Creates an arc segment of varying angles.
*   `x_line`: Creates a rectangular segment.
*  `circle_maze`: Assembles the maze from the maze generation vector.
*   `hollow_sphere`: Creates a hollow sphere shape.
*   `gyro_base`: creates the central sphere with flat sides.
*   `gyro_base_with_mazes`: Puts the maze patterns onto the gyro base.
*  `maze_gyro`: Assembles all components.

Also, I will need the helper functions to achieve a recursive backtracker maze generator:
*   `cord_equals`: Checks if coordinates are equivalent.
*   `not_visited`: Checks if a coordinate has been visited.
*   `rand_dirs`: Returns a set of random directions.
*   `replace`: Replaces a vector with another vector in a vector of vectors.
*   `init_maze`: initializes the maze vector.
*   `find`: Finds a vector in the maze vector by coordinates.
*  `delete_right_wall`: Deletes a right wall from the block.
*   `delete_up_wall`: Deletes a up wall from the block.
*   `delete_right_wall_of`: Deletes a right wall from a maze block.
*   `delete_up_wall_of`: Deletes a up wall from a maze block.
*   `go_right`: Moves the generator one step right.
*   `go_up`: Moves the generator one step up.
*  `visit`: Marks a node as visited.
*   `go_left`: Moves the generator one step left.
*   `go_down`: Moves the generator one step down.
*   `go_maze`: Generates the maze with a recursive backtracker.
*   `look_around`: Makes a decision on which way to go.
*   `build_wall`: Decides whether to move or not and moves.

And also the constants:
*   `PERMUTATION_OF_FOUR`: A permutation of the four possible directions.
*  `NO_WALL`, `UP_WALL`, `RIGHT_WALL`, `UP_RIGHT_WALL`: Constants for the different wall configurations in a single block.
*  `NOT_VISITED`, `VISITED`: Constants for the state of a block in the maze.

I don't need any special libraries beyond the standard OpenSCAD features.

Could you generate the OpenSCAD code for this?

[Assistant]
maze_gyro.scad:
```
radius = 20;

spacing = 0.6;

wall_height = 1;
wall_thickness = 1.5;
cblocks = 25;
rblocks = 4;

ring = "YES";

/*
 * constants, for clearness
 *
 */

// random directions, for picking up a direction to visit the next block
function PERMUTATION_OF_FOUR() = [
    [1, 2, 3, 4],
    [1, 2, 4, 3],
    [1, 3, 2, 4],
    [1, 3, 4, 2],
    [1, 4, 2, 3],
    [1, 4, 3, 2],
    [2, 1, 3, 4],
    [2, 1, 4, 3],
    [2, 3, 1, 4],
    [2, 3, 4, 1],
    [2, 4, 1, 3],
    [2, 4, 3, 1],
    [3, 1, 2, 4],
    [3, 1, 4, 2],
    [3, 2, 1, 4],
    [3, 2, 4, 1],
    [3, 4, 1, 2],
    [3, 4, 2, 1],
    [4, 1, 2, 3],
    [4, 1, 3, 2],
    [4, 2, 1, 3],
    [4, 2, 3, 1],
    [4, 3, 1, 2],
    [4, 3, 2, 1]
];

function NO_WALL() = 0;
function UP_WALL() = 1;
function RIGHT_WALL() = 2;
function UP_RIGHT_WALL() = 3;

function NOT_VISITED() = 0;
function VISITED() = 1;

/*
 * modules for creating a maze
 *
 */

function PI() = 3.14159;

module a_quarter_arc(radius, angle, width = 1) {
    outer = radius + width;
    intersection() {
        difference() {
            offset(r = width) circle(radius, $fn=96);
            circle(radius, $fn=96);
        }
        polygon([[0, 0], [outer, 0], [outer, outer * sin(angle)], [outer * cos(angle), outer * sin(angle)]]);
    }
}

module arc(radius, angles, width = 1) {
    angle_from = angles[0];
    angle_to = angles[1];
    angle_difference = angle_to - angle_from;
    outer = radius + width;
    rotate(angle_from)
        if(angle_difference <= 90) {
            a_quarter_arc(radius, angle_difference, width);
        } else if(angle_difference > 90 && angle_difference <= 180) {
            arc(radius, [0, 90], width);
            rotate(90) a_quarter_arc(radius, angle_difference - 90, width);
        } else if(angle_difference > 180 && angle_difference <= 270) {
            arc(radius, [0, 180], width);
            rotate(180) a_quarter_arc(radius, angle_difference - 180, width);
        } else if(angle_difference > 270 && angle_difference <= 360) {
            arc(radius, [0, 270], width);
            rotate(270) a_quarter_arc(radius, angle_difference - 270, width);
       }
}

/*
 * utilities functions
 *
 */

// comare the equality of [x1, y1] and [x2, y2]
function cord_equals(cord1, cord2) = cord1 == cord2;

// is the point visited?
function not_visited(cord, vs, index = 0) =
    index == len(vs) ? true :
        (cord_equals([vs[index][0], vs[index][1]], cord) && vs[index][2] == 1 ? false :
            not_visited(cord, vs, index + 1));

// pick a direction randomly
function rand_dirs() =
    PERMUTATION_OF_FOUR()[round(rands(0, 24, 1)[0])];

// replace v1 in the vector with v2
function replace(v1, v2, vs) =
    [for(i = [0:len(vs) - 1]) vs[i] == v1 ? v2 : vs[i]];

/*
 * functions for generating a maze vector
 *
 */

// initialize a maze
function init_maze(rows, columns) =
    [
	    for(c = [1 : columns])
	        for(r = [1 : rows])
		        [c, r, 0,  UP_RIGHT_WALL()]
	];

// find a vector in the maze vector
function find(i, j, maze_vector, index = 0) =
    index == len(maze_vector) ? [] : (
        cord_equals([i, j], [maze_vector[index][0], maze_vector[index][1]]) ? maze_vector[index] : find(i, j, maze_vector, index + 1)
    );

////
// NO_WALL = 0;
// UP_WALL = 1;
// RIGHT_WALL = 2;
// UP_RIGHT_WALL = 3;
function delete_right_wall(original_block) =
    original_block == NO_WALL() || original_block == RIGHT_WALL() ? NO_WALL() : UP_WALL();

function delete_up_wall(original_block) =
    (original_block == NO_WALL() || original_block == UP_WALL()) ? NO_WALL() : RIGHT_WALL();

function delete_right_wall_of(vs, is_visited, maze_vector) =
    replace(vs, [vs[0], vs[1] , is_visited, delete_right_wall(vs[3])] ,maze_vector);

function delete_up_wall_of(vs, is_visited, maze_vector) =
    replace(vs, [vs[0], vs[1] , is_visited, delete_up_wall(vs[3])] ,maze_vector);

function go_right(i, j, rows, columns, maze_vector) =
    go_maze(i + 1, j, rows, columns, delete_right_wall_of(find(i, j, maze_vector), VISITED(), maze_vector));

function go_up(i, j, rows, columns, maze_vector) =
    go_maze(i, j - 1, rows, columns, delete_up_wall_of(find(i, j, maze_vector), VISITED(), maze_vector));

function visit(v, maze_vector) =
    replace(v, [v[0], v[1], VISITED(), v[3]], maze_vector);

function go_left(i, j, rows, columns, maze_vector) =
    go_maze(i - 1, j, rows, columns, delete_right_wall_of(find(i - 1, j, maze_vector), NOT_VISITED(), maze_vector));

function go_down(i, j, rows, columns, maze_vector) =
    go_maze(i, j + 1, rows, columns, delete_up_wall_of(find(i, j + 1, maze_vector), NOT_VISITED(), maze_vector));

function go_maze(i, j, rows, columns, maze_vector) =
    look_around(i, j, rand_dirs(), rows, columns, visit(find(i, j, maze_vector), maze_vector));

function look_around(i, j, dirs, rows, columns, maze_vector, index = 0) =
    index == 4 ? maze_vector :
        look_around(
            i, j, dirs,
            rows, columns,
            build_wall(i, j, dirs[index], rows, columns, maze_vector),
            index + 1
        );

function build_wall(i, j, n, rows, columns, maze_vector) =
    n == 1 && i != columns && not_visited([i + 1, j], maze_vector) ? go_right(i, j, rows, columns, maze_vector) : (
        n == 2 && j != 1 && not_visited([i, j - 1], maze_vector) ? go_up(i, j, rows, columns, maze_vector)  : (
            n == 3 && i != 1 && not_visited([i - 1, j], maze_vector) ? go_left(i, j,rows, columns,  maze_vector)  : (
                n == 4 && j != rows && not_visited([i, j + 1], maze_vector) ? go_down(i, j, rows, columns, maze_vector) : maze_vector
            )
        )
    );

module x_line(point, length, thickness = 1) {
    offset = thickness / 2;
    translate([point[0] - offset, point[1] - offset, 0])
        square([length + thickness, thickness]);
}

module circle_maze(radius, cblocks, rblocks, thickness = 1) {
    full_circle_angle = 360;
    arc_angle = full_circle_angle / cblocks;
	length_rblock = radius / rblocks ;
	rows = rblocks - 2;

	maze = go_maze(1, 1, cblocks, rows, replace([rows, cblocks, 0, UP_RIGHT_WALL()], [rows, cblocks, 0, UP_WALL()], init_maze(cblocks, rows)));

	// inner circle
	rotate([0, 0, 180])
	    arc(length_rblock, [0, full_circle_angle - arc_angle], thickness);

	rotate([0, 0, 315])
	    x_line([length_rblock + thickness / 2, 0], length_rblock, thickness);

	rotate([0, 0, arc_angle])
	    arc(length_rblock * 2, [0, full_circle_angle - arc_angle], thickness);

	// maze
    for(i = [0:len(maze) - 1]) {
        cord = maze[i];
		cr = cord[0] + 2;
		cc = cord[1] - 1;
        v = cord[3];

        if(v == 1 || v == 3) {
		    rotate([0, 0, cc * arc_angle])
                x_line([(cr - 1)* length_rblock + thickness / 2, 0], length_rblock, thickness);
        }
        if(v == 2 || v == 3) {
		    rotate([0, 0, cc * arc_angle])
		        arc(cr * length_rblock, [0, arc_angle + 0.01], thickness);
        }

		if(v == 0 || v == 1) {
		    r1 = length_rblock * 2;
			r2 = cr * length_rblock;
		    rotate([0, 0, cc * arc_angle])
		        arc(cr * length_rblock, [0, arc_angle * (r2 - r1) / r2], thickness);
		}
    }
}

module hollow_sphere(radius, thickness = 1) {
    difference() {
	    sphere(radius + thickness, $fn = 96);
        sphere(radius, $fn = 96);
	}
}

// a gyro base. Its height is equals to `radius`.
module gyro_base(radius) {
    double_radius = radius * 2;
	half_height = radius / 3;
	difference() {
		sphere(radius, $fn = 96);
		translate([0, 0, -radius - half_height])
		    cube(double_radius, center = true);
		translate([0, 0, radius + half_height])
		    cube(double_radius, center = true);
	}
}

// create mazes on two sides of the gyro base
module gyro_base_with_mazes(radius, cblocks, rblocks, wall_height = 1, wall_thickness = 1) {
    height = radius * 2 / 3;

    r = sqrt(pow(radius, 2) - pow(height / 2, 2));

	difference() {
	    gyro_base(radius);

		translate([0, 0, height / 2 - wall_height])
		    linear_extrude(wall_height)
		         circle(r, $fn = 96);

	    translate([0, 0, -height / 2])
		    linear_extrude(wall_height)
		         circle(r, $fn = 96);
	}

    translate([0, 0, height / 2 - wall_height])
        linear_extrude(wall_height)
            circle_maze(r - wall_thickness, cblocks, rblocks, wall_thickness);

    translate([0, 0, -height / 2 - wall_thickness / 2 + wall_height])
        linear_extrude(wall_height)
            circle_maze(r - wall_thickness, cblocks, rblocks, wall_thickness);
}

// use hollow spheres to cut out a space between each gyro circle
module maze_gyro(radius, cblocks, rblocks, ring = "YES", spacing = 0.6, wall_height = 1, wall_thickness = 1) {
    height = radius * 2 / 3;
    r = sqrt(pow(radius, 2) - pow(wall_height, 2));
	one_half_wall_thickness = 1.5 * wall_thickness;
    length_rblock = (r - one_half_wall_thickness) / rblocks;

	difference() {
		gyro_base_with_mazes(radius, cblocks, rblocks, wall_height, wall_thickness);

		for(i = [2 : rblocks]) {
			r_hollow_sphere = sqrt(pow(length_rblock * (i - 1) + spacing * (rblocks - i + 3), 2) + pow(height / 2, 2));
			hollow_sphere(r_hollow_sphere, spacing);
		}
	}

	// ring
	if(ring == "YES") {
	    difference() {
	        translate([radius, 0, -height / 8]) rotate([0, 0, -90])
	            linear_extrude(height / 4) arc(length_rblock, [0, 360], height / 8);
			gyro_base(radius);
	    }
	}
}

maze_gyro(radius, cblocks, rblocks, ring, spacing, wall_height, wall_thickness);

```


[User]
I'm looking to create a customizable nozzle holder for 3D printer nozzles, specifically E3D nozzles. I need an OpenSCAD design that allows me to specify a grid layout for the nozzles, and each nozzle location should have a threaded hole to hold the nozzle. I also want to be able to add a text label to each location to indicate the nozzle size.

Here are the specific requirements:

*   **Grid Layout:** I need to be able to define a grid of nozzle holders. The grid should be defined by a 2D array of nozzle sizes. Each row of the array will represent a row of holders. Some locations may be empty, and the number of nozzles in each row might vary.
*   **Threaded Holes:** Each nozzle location should have a threaded hole.  I need to use the metric thread library from http://dkprojects.net/openscad-threads/ for this. The threads should be M6.5x1.
*   **Text Labels:** Each nozzle location should have a text label indicating the nozzle size, centered below the threads.
*   **Customizable Dimensions:** I need to be able to customize the following:
    *   The width and height of each tile (the area for one nozzle holder), e.g. tile_width and tile_height
    *   The overall height of the nozzle holder, e.g. holder_height.
*   **Library Dependency:** It needs to use the `threads.scad` library from the URL above. This should be included using `use`.

I'm thinking the code should take a 2D array like `[["0.25","0.3","0.35"],["0.4","0.6","0.8"]]` where each element is a nozzle size as text. It should then generate a holder that matches that layout, and each nozzle location should have the thread and the text label. Empty array locations should be empty, but have the same dimensions as the nozzle holders.

Can you generate the OpenSCAD file(s) for this? I don't need any example code using the library, the library itself or code to download the library. I expect that to be handled externally.

[Assistant]
nozzle_holder_for_creator.scad:
```
// you need this library from http://dkprojects.net/openscad-threads/
//use </home/oja/threads.scad>

// Written by Ondrej Janovsky <oj@alarex.cz>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to the
// public domain worldwide. This software is distributed without any
// warranty.
//
// You should have received a copy of the CC0 Public Domain
// Dedication along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

 //////////////////////////
 // Customizer Settings: //
//////////////////////////
/* [Dimensions] */
//[[0.25,0.30,0.40],[0.60,0.80]] - only numbers, characters are not allowed!
holes_vector = [[0.15,0.25,0.30,0.35],[0.40,0.60,0.80]];
tile_width = 20; //
tile_height = 20; //
holder_height = 5; //

/* [Hidden] */
if (holes_vector!=0) {
createHolder(holes_vector,tile_width,tile_height,holder_height);
} else {
    cube([1,1,1]);
}

module holder_tile(text, height=5, width_x = 15, width_y= 15){
    difference(){
       cube([width_x,width_y,height]);
       translate([(width_x/2),width_y-5,0]) metric_thread(diameter=6.5, pitch=1, length=height, internal=true);
       translate([(width_x/2),width_y-15, height-0.8]) linear_extrude(height=1) text(text=str(text), size=5, halign="center", font="Liberation Sans:style=Bold");
    }
}

module empty_tile(height=5, width_x = 15, width_y= 15){
   cube([width_x,width_y,height]);
}

//calculate max. number of columns in array - recursion
function computeColumns(array,line,num_max=0) = line<len(array)? computeColumns(array,line+1,num_max<len(array[line]) ?len(array[line]):num_max ) : num_max<len(array[line]) ?len(array[line]):num_max;

module createHolder(holes=[], tile_width_x=20, tile_width_y=20, total_height=5) {
    columns = computeColumns(holes,0,0);

    echo("Maximum columns: ",columns);

    i=0;
    for(line_index=[0:len(holes)-1]){
        line = holes[line_index];
        for(index=[0:columns-1]) {
            yy = -line_index*tile_width_y + 1;
            xx = index*tile_width_x;
            cell = line[index];
            if(cell!=undef) {
                    translate([xx,yy,0]) holder_tile(text = cell, height=total_height, width_x=tile_width_x, width_y=tile_width_y);
            }  else {
                 translate([xx,yy,0]) empty_tile(height=total_height, width_x=tile_width_x, width_y=tile_width_y);
            }
        }
    }
}

//use this
//vector contains vectors - lines with holes. Number of holes can be different for each line.
//holes = [["0.25","0.3","0.35"],["0.4","0.6","0.8"]];
//createHolder(holes,20,20,5);

/*
 * ISO-standard metric threads, following this specification:
 *          http://en.wikipedia.org/wiki/ISO_metric_screw_thread
 *
 * Dan Kirshner - dan_kirshner@yahoo.com
 *
 * You are welcome to make free use of this software.  Retention of my
 * authorship credit would be appreciated.
 *
 * Version 1.8.  2016-01-08  Option: (non-standard) angle.
 * Version 1.7.  2015-11-28  Larger x-increment - for small-diameters.
 * Version 1.6.  2015-09-01  Options: square threads, rectangular threads.
 * Version 1.5.  2015-06-12  Options: thread_size, groove.
 * Version 1.4.  2014-10-17  Use "faces" instead of "triangles" for polyhedron
 * Version 1.3.  2013-12-01  Correct loop over turns -- don't have early cut-off
 * Version 1.2.  2012-09-09  Use discrete polyhedra rather than linear_extrude ()
 * Version 1.1.  2012-09-07  Corrected to right-hand threads!
 */

// Examples.
//
// Standard M8 x 1.
// metric_thread (diameter=8, pitch=1, length=4);

// Square thread.
// metric_thread (diameter=8, pitch=1, length=4, square=true);

// Non-standard: long pitch, same thread size.
//metric_thread (diameter=8, pitch=4, length=4, thread_size=1, groove=true);

// Non-standard: 20 mm diameter, long pitch, square "trough" width 3 mm,
// depth 1 mm.
//metric_thread (diameter=20, pitch=8, length=16, square=true, thread_size=6,
//               groove=true, rectangle=0.333);

// English: 1/4 x 20.
//english_thread (diameter=1/4, threads_per_inch=20, length=1);

// Thread for mounting on Rohloff hub.
//difference () {
//   cylinder (r=20, h=10, $fn=100);
//
//   metric_thread (diameter=34, pitch=1, length=10, internal=true, n_starts=6);
//}

// ----------------------------------------------------------------------------
function segments (diameter) = min (50, ceil (diameter*6));

// ----------------------------------------------------------------------------
// internal -    true = clearances for internal thread (e.g., a nut).
//               false = clearances for external thread (e.g., a bolt).
//               (Internal threads should be "cut out" from a solid using
//               difference ()).
// n_starts -    Number of thread starts (e.g., DNA, a "double helix," has
//               n_starts=2).  See wikipedia Screw_thread.
// thread_size - (non-standard) size of a single thread "V" - independent of
//               pitch.  Default: same as pitch.
// groove      - (non-standard) subtract inverted "V" from cylinder (rather than
//               add protruding "V" to cylinder).
// square      - Square threads (per
//               https://en.wikipedia.org/wiki/Square_thread_form).
// rectangle   - (non-standard) "Rectangular" thread - ratio depth/width
//               Default: 1 (square).
// angle       - (non-standard) angle (deg) of thread side from perpendicular to
//               axis (default = standard = 30 degrees).
module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1,
                      thread_size=-1, groove=false, square=false, rectangle=0,
                      angle=30)
{
   // thread_size: size of thread "V" different than travel per turn (pitch).
   // Default: same as pitch.
   local_thread_size = thread_size == -1 ? pitch : thread_size;
   local_rectangle = rectangle ? rectangle : 1;

   n_segments = segments (diameter);
   h = (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size * cos (angle);

   h_fac1 = (square || rectangle) ? 0.90 : 0.625;

   // External thread includes additional relief.
   h_fac2 = (square || rectangle) ? 0.95 : 5.3/8;

   if (! groove) {
      metric_thread_turns (diameter, pitch, length, internal, n_starts,
                           local_thread_size, groove, square, rectangle, angle);
   }

   difference () {

      // Solid center, including Dmin truncation.
      if (groove) {
         cylinder (r=diameter/2, h=length, $fn=n_segments);
      } else if (internal) {
         cylinder (r=diameter/2 - h*h_fac1, h=length, $fn=n_segments);
      } else {

         // External thread.
         cylinder (r=diameter/2 - h*h_fac2, h=length, $fn=n_segments);
      }

      if (groove) {
         metric_thread_turns (diameter, pitch, length, internal, n_starts,
                              local_thread_size, groove, square, rectangle,
                              angle);
      }
   }
}

// ----------------------------------------------------------------------------
// Input units in inches.
// Note: units of measure in drawing are mm!
module english_thread (diameter=0.25, threads_per_inch=20, length=1,
                      internal=false, n_starts=1, thread_size=-1, groove=false,
                      square=false, rectangle=0)
{
   // Convert to mm.
   mm_diameter = diameter*25.4;
   mm_pitch = (1.0/threads_per_inch)*25.4;
   mm_length = length*25.4;

   echo (str ("mm_diameter: ", mm_diameter));
   echo (str ("mm_pitch: ", mm_pitch));
   echo (str ("mm_length: ", mm_length));
   metric_thread (mm_diameter, mm_pitch, mm_length, internal, n_starts,
                  thread_size, groove, square, rectangle);
}

// ----------------------------------------------------------------------------
module metric_thread_turns (diameter, pitch, length, internal, n_starts,
                            thread_size, groove, square, rectangle, angle)
{
   // Number of turns needed.
   n_turns = floor (length/pitch);

   intersection () {

      // Start one below z = 0.  Gives an extra turn at each end.
      for (i=[-1*n_starts : n_turns+1]) {
         translate ([0, 0, i*pitch]) {
            metric_thread_turn (diameter, pitch, internal, n_starts,
                                thread_size, groove, square, rectangle, angle);
         }
      }

      // Cut to length.
      translate ([0, 0, length/2]) {
         cube ([diameter*3, diameter*3, length], center=true);
      }
   }
}

// ----------------------------------------------------------------------------
module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size,
                           groove, square, rectangle, angle)
{
   n_segments = segments (diameter);
   fraction_circle = 1.0/n_segments;
   for (i=[0 : n_segments-1]) {
      rotate ([0, 0, i*360*fraction_circle]) {
         translate ([0, 0, i*n_starts*pitch*fraction_circle]) {
            thread_polyhedron (diameter/2, pitch, internal, n_starts,
                               thread_size, groove, square, rectangle, angle);
         }
      }
   }
}

// ----------------------------------------------------------------------------
// z (see diagram) as function of current radius.
// (Only good for first half-pitch.)
function z_fct (current_radius, radius, pitch, angle)
   = 0.5* (current_radius - (radius - 0.875*pitch*cos (angle)))
                                                       /cos (angle);

// ----------------------------------------------------------------------------
module thread_polyhedron (radius, pitch, internal, n_starts, thread_size,
                          groove, square, rectangle, angle)
{
   n_segments = segments (radius*2);
   fraction_circle = 1.0/n_segments;

   local_rectangle = rectangle ? rectangle : 1;

   h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size * cos (angle);
   outer_r = radius + (internal ? h/20 : 0); // Adds internal relief.
   //echo (str ("outer_r: ", outer_r));

   // A little extra on square thread -- make sure overlaps cylinder.
   h_fac1 = (square || rectangle) ? 1.1 : 0.875;
   inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with
                                // cylinder.

   translate_y = groove ? outer_r + inner_r : 0;
   reflect_x   = groove ? 1 : 0;

   // Make these just slightly bigger (keep in proportion) so polyhedra will
   // overlap.
   x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02;
   x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02;
   z_incr = n_starts * pitch * fraction_circle * 1.005;

   /*
    (angles x0 and x3 inner are actually 60 deg)

                          /\  (x2_inner, z2_inner) [2]
                         /  \
   (x3_inner, z3_inner) /    \
                  [3]   \     \
                        |\     \ (x2_outer, z2_outer) [6]
                        | \    /
                        |  \  /|
             z          |[7]\/ / (x1_outer, z1_outer) [5]
             |          |   | /
             |   x      |   |/
             |  /       |   / (x0_outer, z0_outer) [4]
             | /        |  /     (behind: (x1_inner, z1_inner) [1]
             |/         | /
    y________|          |/
   (r)                  / (x0_inner, z0_inner) [0]

   */

   x1_outer = outer_r * fraction_circle * 2 * PI;

   z0_outer = z_fct (outer_r, radius, thread_size, angle);
   //echo (str ("z0_outer: ", z0_outer));

   //polygon ([[inner_r, 0], [outer_r, z0_outer],
   //        [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]);
   z1_outer = z0_outer + z_incr;

   // Give internal square threads some clearance in the z direction, too.
   bottom = internal ? 0.235 : 0.25;
   top    = internal ? 0.765 : 0.75;

   translate ([0, translate_y, 0]) {
      mirror ([reflect_x, 0, 0]) {

         if (square || rectangle) {

            // Rule for face ordering: look at polyhedron from outside: points must
            // be in clockwise order.
            polyhedron (
               points = [
                         [-x_incr_inner/2, -inner_r, bottom*thread_size],         // [0]
                         [x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1]
                         [x_incr_inner/2, -inner_r, top*thread_size + z_incr],    // [2]
                         [-x_incr_inner/2, -inner_r, top*thread_size],            // [3]

                         [-x_incr_outer/2, -outer_r, bottom*thread_size],         // [4]
                         [x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5]
                         [x_incr_outer/2, -outer_r, top*thread_size + z_incr],    // [6]
                         [-x_incr_outer/2, -outer_r, top*thread_size]             // [7]
                        ],

               faces = [
                         [0, 3, 7, 4],  // This-side trapezoid

                         [1, 5, 6, 2],  // Back-side trapezoid

                         [0, 1, 2, 3],  // Inner rectangle

                         [4, 7, 6, 5],  // Outer rectangle

                         // These are not planar, so do with separate triangles.
                         [7, 2, 6],     // Upper rectangle, bottom
                         [7, 3, 2],     // Upper rectangle, top

                         [0, 5, 1],     // Lower rectangle, bottom
                         [0, 4, 5]      // Lower rectangle, top
                        ]
            );
         } else {

            // Rule for face ordering: look at polyhedron from outside: points must
            // be in clockwise order.
            polyhedron (
               points = [
                         [-x_incr_inner/2, -inner_r, 0],                        // [0]
                         [x_incr_inner/2, -inner_r, z_incr],                    // [1]
                         [x_incr_inner/2, -inner_r, thread_size + z_incr],      // [2]
                         [-x_incr_inner/2, -inner_r, thread_size],              // [3]

                         [-x_incr_outer/2, -outer_r, z0_outer],                 // [4]
                         [x_incr_outer/2, -outer_r, z0_outer + z_incr],         // [5]
                         [x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6]
                         [-x_incr_outer/2, -outer_r, thread_size - z0_outer]    // [7]
                        ],

               faces = [
                         [0, 3, 7, 4],  // This-side trapezoid

                         [1, 5, 6, 2],  // Back-side trapezoid

                         [0, 1, 2, 3],  // Inner rectangle

                         [4, 7, 6, 5],  // Outer rectangle

                         // These are not planar, so do with separate triangles.
                         [7, 2, 6],     // Upper rectangle, bottom
                         [7, 3, 2],     // Upper rectangle, top

                         [0, 5, 1],     // Lower rectangle, bottom
                         [0, 4, 5]      // Lower rectangle, top
                        ]
            );
         }
      }
   }
}

```
nozzle_holder.scad:
```
// you need this library from http://dkprojects.net/openscad-threads/
use </home/oja/threads.scad>

// Written by Ondrej Janovsky <oj@alarex.cz>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to the
// public domain worldwide. This software is distributed without any
// warranty.
//
// You should have received a copy of the CC0 Public Domain
// Dedication along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

module holder_tile(text, height=5, width_x = 15, width_y= 15){
    difference(){
       cube([width_x,width_y,height]);
       translate([(width_x/2),width_y-5,0]) metric_thread(diameter=6.5, pitch=1, length=height, internal=true);
       translate([(width_x/2),width_y-15, height-0.8]) linear_extrude(height=0.8) text(text=text, size=5, halign="center", font="Liberation Sans:style=Bold");
    }
}

module empty_tile(height=5, width_x = 15, width_y= 15){
   cube([width_x,width_y,height]);
}

//calculate max. number of columns in array - recursion
function computeColumns(array,line,num_max=0) = line<len(holes)? computeColumns(array,line+1,num_max<len(array[line]) ?len(array[line]):num_max ) : num_max<len(array[line]) ?len(array[line]):num_max;

module createHolder(holes=[], tile_width_x=20, tile_width_y=20, total_height=5) {
    columns = computeColumns(holes,0,0);

    echo("Maximum columns: ",columns);

    i=0;
    for(line_index=[0:len(holes)-1]){
        line = holes[line_index];
        for(index=[0:columns-1]) {
            yy = -line_index*tile_width_y + 1;
            xx = index*tile_width_x;
            cell = line[index];
            if(cell!=undef) {
                    translate([xx,yy,0]) holder_tile(text = cell, height=total_height, width_x=tile_width_x, width_y=tile_width_y);
            }  else {
                 translate([xx,yy,0]) empty_tile(height=total_height, width_x=tile_width_x, width_y=tile_width_y);
            }
        }
    }
}

//use this
//vector contains vectors - lines with holes. Number of holes can be different for each line.
holes = [["0.25","0.3","0.35"],["0.4","0.6","0.8"]];
createHolder(holes,20,20,5);

```


[User]
Hi, I'm looking for a script to generate adapters for my woodworking tools and dust collector. I have a few different tools with different sized ports, so I need a parametric design that I can tweak.

Basically, the adapter needs to fit over the outside diameter of two pipes. So, I need to input the outside diameters of both pipes, and then a length for each side of the adapter. There should also be a transition length that connects the two cylinders. I'd like to be able to specify the wall thickness of the adapter as well. It would be great if the internal diameter of the adapter was slightly larger than the outside diameter of the pipe, so that it can be pressed on. I was thinking a factor to multiply the pipe diameter by should work. Also, a small internal taper on each side would help with the press fit.

So, to summarize, I need a parametric OpenSCAD script with parameters for:

*   Outside diameter of pipe 1
*   Adapter length for pipe 1
*   Outside diameter of pipe 2
*   Adapter length for pipe 2
*   Transition length
*   Wall thickness
*   Clearance factor (for the internal diameter)
*   Internal taper angle

I guess it would be great to also be able to adjust the scaling value to compensate for print shrinkage. I am going to use ABS, but maybe the default should be more for PLA, something like 1/0.998.

The adapter should be a hollow cylinder with the given dimensions and the defined transitions. The cylinders should have smooth, circular cross-sections (as good as can be achieved in OpenSCAD anyway).

I’m not sure if this makes a difference but I’m running OpenSCAD on Linux. I don’t have any external OpenSCAD libraries that I need to use, though I'm open to suggestions if you think they will be beneficial to this project.

[Assistant]
adapter.scad:
```
//------------------------------------------------------------------
/*

Adapters for connecting a tool to a dust collector.

The adapter fits around the outside diameter of the pipes on the
tool and the vaccuum. Measure the diameter of the pipes with a set
of calipers. Enter the values and the script will take care of
the rest.

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

// outside diameter of pipe 1 (mm)
od_pipe_1 = 57.4;
// adapter length for pipe 1 (mm)
len_pipe_1 = 40;
// outside diameter of pipe 1 (mm)
od_pipe_2 = 42.1;
// adapter length for pipe 2 (mm)
len_pipe_2 = 30;
// transition length (mm)
transition_length = 20;
// wall thickness (mm)
wall_thickness = 4;
// The adapter ID will be larger than the pipe OD by this factor
clearance = 1.017;
// internal taper (degrees)
taper = 1;

//------------------------------------------------------------------
// Set the scaling value to compensate for print shrinkage

scale = 1/0.995; // ABS ~0.5% shrinkage
//scale = 1/0.998; // PLA ~0.2% shrinkage

function dim(x) = scale * x;

//------------------------------------------------------------------
// derived values

r1 = dim(od_pipe_1) / 2;
r2 = dim(od_pipe_2) / 2;

len1 = dim(len_pipe_1);
len2 = dim(len_pipe_2);

tlen = dim(transition_length);

wt = dim(wall_thickness);

//------------------------------------------------------------------
// control the number of facets on cylinders

facet_epsilon = 0.01;
function facets(r) = 180 / acos(1 - (facet_epsilon / r));

//------------------------------------------------------------------
// generate a 2D polygon for the adapter wall

module adapter_wall() {

  ir1 = r1 * clearance;
  ir2 = r2 * clearance;
  or1 = ir1 + wt;
  or2 = ir2 + wt;
  t1 = len1 * tan(taper);
  t2 = len2 * tan(taper);

  echo("nominal od1 is ", or1 * 2, "mm");
  echo("nominal od2 is ", or2 * 2, "mm");

  points = [
    [ir1, 0],
    [or1, 0],
    [or1, len1],
    [or2, len1 + tlen],
    [or2, len1 + tlen + len2],
    [ir2, len1 + tlen + len2],
    [ir2 - t2, len1 + tlen],
    [ir1 - t1, len1],
  ];
  polygon(points=points, convexity = 2);
}

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

module adapter() {
  overhang_angle = atan2(abs(r2 - r1), transition_length);
  echo("overhang angle is ", overhang_angle, "degrees");
  rotate_extrude(angle = 360, $fn = facets(r1)) {
    adapter_wall();
  }
}

adapter();

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

```


[User]
I'm looking to create a customizable, single-sided token for 3D printing. I want to be able to define the overall token dimensions, including width, thickness, and a recessed area on one side. The crucial part is that I also need to be able to take a 2D shape defined by a series of points, like the output from the Stencil Factory tool (http://chaaawa.com/stencil_factory/), and embed that shape into the recessed area of the token.

Here's what I need to be customizable:

*   **Token Size:**
    *   `token_width`: The overall diameter of the token.
    *   `token_rim_size`: The width of the rim surrounding the recessed area.
    *   `token_thickness`: The overall thickness of the token.
    *   `token_recess`: The depth of the recessed area.

*   **Stencil Input:**
    *   A variable to hold the point data generated by Stencil Factory as a list of coordinates (e.g., `points_array`).
    *  `input`: The raw point data string.
    * `stencil_thickness`: The thickness of the extruded stencil shape.
    *  `stencil_width`: The width of the stencil object.
    *   `margin`: margin around the stencil shape.
*   **Stencil Positioning:**
    *  `offX` The horizontal offset of the stencil.
    * `offY` The vertical offset of the stencil.

I'd like the final token to have a circular base with the recess on one side. The Stencil Factory shape should be extruded within this recess. I need the SCAD file to be parametric, so I can change these variables and regenerate the design. I would also like to handle no input gracefully.

It would be great if the code also included comments to explain how the different parts are created. I want to avoid the need for any external libraries if possible.

Could you generate an OpenSCAD file that does this?

[Assistant]
single_sided_token.scad:
```
//Customizable Double Sided Token

//Token Size Variables
token_width = 40;
token_rim_size = 4;
token_thickness = 3;
token_recess = 1;

// Go to: http://chaaawa.com/stencil_factory/, then paste here after "Convert&Copy".
input ="no_input";

stencil_thickness = 1;
stencil_width = 45;
margin = 10;

// If you don't want to center
offX = 0;
offY = -1.5;

module void() {}

/* [hidden] */
dispo_width = stencil_width - 2*margin;

points_array = (input=="no_input"? [[238,300],[[121.78,-1],[121.24,-0.34],[121.34,1.25],[121.27,3.52],[120.15,5.82],[118.24,7.54],[115.76,6.94],[113.37,6.29],[111.31,6.44],[109.5,7.13],[111.25,8.12],[112.57,9.8],[113,12.97],[113.64,16.66],[116,19.85],[118.12,22.4],[119,24.34],[118.82,25.86],[118.39,27.42],[117.56,28.54],[116.39,29],[115.41,28.53],[115,27.42],[114.63,26.42],[113.75,26.24],[112.77,27.91],[112.19,31.41],[112.44,35.93],[113.86,39.96],[115.84,43.75],[113.42,44.85],[111.71,45.93],[111,46.98],[111.66,47.71],[113.25,48.04],[115.66,48.73],[118.29,50.36],[120.44,52.72],[120.04,55.39],[119.5,57.94],[120.04,60.07],[121.1,61.43],[122.21,62],[123.54,61.52],[125.13,60.38],[126.83,59.4],[128.38,59.32],[129.63,61.25],[130.49,65.69],[131.07,70.17],[131.55,72.77],[132.5,73.56],[134.48,73.42],[136.56,73.32],[137,75.22],[136.28,77.61],[134.55,79.95],[132.08,81.58],[129.55,81.77],[127.69,81.73],[127,82.44],[127.35,83.61],[128.2,84.8],[129.67,85.65],[131.51,86],[133.57,85.57],[135.56,84.53],[137.44,83.49],[139.25,83.03],[140.49,83.48],[141,84.59],[140.41,85.87],[139,86.71],[137.59,87.33],[137,88.09],[137.75,89.19],[139.55,90.63],[141.25,92.15],[141.05,93.35],[140.31,94.33],[140,95.2],[140.44,95.77],[141.5,96],[142.56,96.45],[143,97.52],[142.27,98.93],[140.5,100.18],[137.97,100.81],[135.37,100.12],[133.4,99.39],[132.35,99.32],[132.52,99.97],[133.48,100.98],[134.55,102.08],[135,102.96],[133.75,103.84],[130.75,104.94],[127.22,106.3],[124.71,107.79],[122.16,108.89],[116.71,108.11],[110.56,106.45],[104.5,104.24],[98.2,102.16],[91.09,101.56],[85.54,101.81],[83.3,102.62],[83.63,103.57],[84.96,104.28],[86.4,104.96],[87,105.84],[86.41,106.75],[85,107.5],[83.27,108.54],[81.92,110.14],[81.45,111.88],[83.01,112.71],[84.57,113.6],[84.49,115.39],[83.86,117.69],[83.29,120.25],[83.22,122.22],[83.83,123],[84.75,122.72],[85.47,122.04],[86.69,121.38],[88.78,121.12],[92.14,120.94],[96.38,120.44],[100.21,120.13],[100.82,121.11],[99.86,122.38],[98.18,123.49],[96.74,124.48],[96.34,125.49],[98.48,126.18],[103.1,126.43],[108.62,125.95],[112.26,124.57],[115,122.77],[115,125.49],[115.34,127.54],[116.75,127.48],[118.59,126.83],[120.56,126.29],[122.4,126.43],[123.93,127.42],[125.66,128.54],[127.96,129],[130.05,128.82],[131.27,128.4],[131.37,127.26],[130.76,125.37],[130.19,123.32],[130.63,121.97],[132.22,121.89],[135,123.5],[137.9,125.26],[140.13,126],[141.51,125.31],[142.18,123.25],[142.93,121.09],[144.62,120.2],[146.33,120.43],[147.37,121.52],[147.5,123.24],[146.95,125.09],[146.46,126.75],[147.49,127.64],[149.18,127.55],[151.01,126.49],[152.55,124.7],[153.54,122.87],[154.51,121.55],[156.01,121],[157.54,121.34],[158.59,122.14],[159.48,122.28],[160.72,120.57],[162.14,117.86],[164.6,120.33],[167.46,123.8],[170.87,128.68],[175.71,134.88],[182.03,141.53],[188.34,147.6],[193.18,152.4],[196.6,156.64],[199.49,161.35],[201.26,165.44],[202,168.2],[201.75,169.47],[201.16,170],[198.68,168.79],[193.92,165.87],[187.53,161.74],[176.99,161.24],[168.14,161.13],[165.11,162.37],[162.59,163.64],[157.18,164],[152.28,164.25],[151.75,165.15],[151.78,167.05],[148.86,170.9],[145.38,174.51],[142.66,176.71],[140.62,177.58],[138.75,177.96],[136.92,177.39],[134.96,175.96],[133.24,174.7],[131.96,174.52],[131.28,175.34],[131,176.47],[132.18,178.36],[135,181],[137.82,183.52],[139,185.14],[138.09,186.66],[135.89,188.95],[132.78,191.79],[134.91,192.47],[137.29,192.76],[140.02,192.5],[143,191.84],[143,194.42],[143.29,196.24],[143.98,197],[144.52,198.21],[144.37,201.35],[144.26,205.09],[144.92,207.86],[146.34,209.41],[148.43,209.43],[150.24,209.26],[151.27,209.67],[151.37,210.93],[150.9,212.79],[149.26,214.84],[146.55,216.54],[143.12,217.58],[139.78,218.02],[136.29,218.58],[132.5,219.89],[129.03,221.89],[126.32,224.12],[124.32,226.67],[122.59,229.48],[121.7,231.94],[122.03,233.43],[122.71,234.77],[123,236.64],[123.32,238.42],[124.09,239.55],[125.18,239.7],[126.29,239.11],[127.6,238.33],[129.2,238],[130.55,238.57],[131,240.3],[131.32,242.25],[132.1,243.7],[133.78,244.02],[136.85,242.48],[140.17,240.08],[143.04,237.58],[145.04,235.76],[146.29,235],[146.79,235.97],[147,238.3],[146.65,240.98],[145.8,242.8],[145.21,243.75],[146.33,244],[148.98,243.28],[152.89,241.55],[156.94,238.86],[159.36,235.65],[160.52,232.94],[161,231.25],[161.54,230.1],[162.83,228.65],[164.32,227.68],[165.33,227.66],[165.8,229.1],[166,231.61],[166.35,234.14],[167.19,235.62],[168.17,235.73],[168.85,234.93],[170.24,232.94],[172.91,230.16],[176.14,227.52],[178.86,225.89],[180.71,224.49],[180.55,221.23],[179.48,217.76],[177.84,214.65],[176.51,212.2],[176.9,210.7],[177.68,209.01],[178,206.49],[177.57,203.73],[176.53,201.44],[175.49,199.53],[175.03,197.67],[175.37,196.48],[176.25,196.23],[177.42,197.15],[178.49,198.81],[179.55,200.36],[180.71,201],[182.07,200.27],[183.59,198.5],[184.94,196.74],[185.87,196.02],[186.73,196.29],[187.91,196.92],[188.82,198.24],[187.99,201.02],[186.96,204.79],[186.34,209.36],[186.11,213.5],[186.01,216.25],[186.01,218.07],[186.02,220],[186,223.4],[185.9,228.75],[185.32,234.72],[183.33,238.1],[181.6,240.46],[182.42,241.93],[184.29,242.42],[187.38,240.52],[190.3,237.94],[192.45,235.44],[193.85,233.72],[194.99,233],[196.62,234.31],[199.22,237.46],[201.83,240.63],[203.5,241.98],[205.31,243.52],[208.25,247.14],[210.9,251.23],[212,253.88],[211.71,256.03],[211.02,258.92],[209.67,262.04],[207.77,264.76],[205.07,267.04],[201.78,269.1],[198.1,271.38],[194.47,274.16],[191.37,277.5],[188.94,281.12],[187.57,284.29],[187,286.57],[187.55,287.74],[189.63,287.56],[192.25,286.9],[191.73,289.64],[191.51,291.95],[191.71,293.69],[192.44,294.61],[193.5,295],[194.58,294.56],[195.36,293.5],[196.14,292.44],[197.22,292.01],[198.79,293.05],[200.76,295.51],[202.95,298.03],[205.02,299],[206.45,298.66],[205.84,297.11],[205.5,294.83],[208.13,291.16],[211.38,287.66],[214.31,284.97],[216.21,283.13],[217.02,281.68],[217.32,280.41],[218.02,279],[219,277.5],[219.98,279],[220.68,281.01],[220.98,283.75],[221.25,286.05],[221.87,287],[223.55,285.63],[226.37,282.34],[229.13,277.99],[230,274.34],[230.28,271.98],[230.96,271],[231.98,271.62],[233.07,273.12],[234.2,275.24],[236.2,273.25],[237.65,270.87],[238.24,267.88],[238.15,265.05],[237.89,263],[238.12,261.89],[239.5,261.5],[241.08,262.08],[241.83,263.81],[242.4,265.33],[243.33,265.74],[244.67,264.39],[246.26,261.68],[247.78,259.08],[248.98,258],[250.11,257.13],[251.47,255.05],[252.55,252.77],[253,251.43],[252.25,251.11],[250.45,251.25],[247.99,251.12],[245.7,249.84],[243.5,247.95],[245.75,246.35],[247.34,244.83],[248,243.41],[247.43,242.16],[246.07,241.04],[244.01,240.3],[241.79,240],[239.48,239.54],[237.21,238.43],[235.79,237.05],[235.54,235.93],[236.67,235.27],[238.56,235],[240.28,234.83],[241,234.43],[240.71,233.49],[240.01,232.02],[238.42,230.51],[236.01,229.5],[233.88,228.88],[233,228.33],[233.59,227.22],[235,225.27],[236.41,223.1],[237,221.46],[236.04,220.61],[232.85,220.75],[229.26,220.82],[227.35,219.46],[226.4,217.47],[226,215.28],[226.44,213.45],[227.51,212.36],[229.01,212.3],[230.47,212.97],[231.81,213.62],[233.01,213.49],[233.63,212.21],[233.5,210],[233.37,207.81],[233.94,206.54],[234.69,205.67],[235,204.5],[234.29,203.53],[232.25,203.31],[230.04,203.03],[229.26,201.5],[229.89,199.12],[231.78,196.19],[234.54,192.89],[239.47,195.64],[244.73,197.85],[250.97,198.45],[257.57,197.87],[264.27,195.18],[270.05,191.7],[274.17,187.93],[277.03,185.1],[279.42,183.99],[281.99,183.68],[285.25,182.95],[287.9,181.88],[289,180.77],[289.57,179.36],[290.93,177.56],[292.39,174.79],[292.13,169.39],[290.83,163.63],[287.7,158.96],[285.09,155.68],[284,153.86],[282.75,152.58],[279.73,150.58],[275.71,147.24],[272.62,140.79],[270.42,133.81],[270.29,127.81],[270.35,122.87],[268.9,120.41],[267.52,118.24],[267,115.35],[267.55,112.37],[269,110],[271.1,108.59],[273.35,108],[275.49,108.54],[277.35,109.83],[278.52,111.98],[279.03,114.58],[279.06,117.5],[280.53,115.56],[281.57,113.05],[282,109.73],[281.36,106.03],[278.96,102.81],[275.93,99.77],[269.51,100.18],[263.51,101.12],[258.98,103.89],[255.97,106.12],[254.5,106.84],[253.63,104.38],[252.04,98.99],[250.38,90.9],[249.33,81.98],[249.2,73.26],[250.39,66.73],[252.48,61.64],[255.18,57.45],[258.45,54.4],[261.91,52.08],[265.62,50.63],[269.5,49.67],[272.72,49.49],[274.86,49.91],[275.69,51.14],[275.49,53.13],[274.41,55.76],[272.8,58.75],[270.8,61.13],[268.75,62],[266.73,61.51],[264.83,60.35],[263,58.69],[263,61.29],[263.61,63.6],[265.22,65.44],[267.53,66.54],[269.97,67],[272.72,66.41],[275.77,65],[278.35,63.59],[279.95,63],[280.91,64.01],[281.98,66.44],[282.59,69.8],[281.53,72.94],[279.82,75.19],[277.88,76],[276.24,75.44],[275.29,74],[274.55,72.59],[273.49,72],[272.41,72.46],[271.61,73.58],[271.18,75.57],[271,78.12],[271.8,80.9],[275.57,83.03],[279.19,85.05],[279.96,87.25],[279.86,89.66],[279.89,92.3],[280.69,94.99],[282.45,97.55],[284.53,99.28],[286.07,100],[286.77,99.14],[286.57,96.51],[286.43,93.37],[287.11,90.76],[288.64,88.33],[290.65,85.67],[292.31,83.09],[293,80.85],[293.42,79.43],[294.73,79.19],[296.24,80.26],[297.46,82.36],[297.81,85.13],[295.67,87.61],[293.88,89.59],[293.5,91],[294.61,91.71],[296.42,92],[298.83,91.39],[301.39,89.91],[303.69,87.6],[305.52,84.96],[306.57,81.45],[307,77.03],[307.43,72.64],[309.47,70.83],[311.74,70.22],[313.47,70.98],[314.55,72.28],[315,73.62],[315.29,74.6],[316,75],[316.71,73.84],[317,71.06],[316.71,68.09],[316,66.5],[315.29,65.61],[315,64.35],[315.59,62.52],[317,60.27],[318.41,57.93],[319,55.87],[318.71,54.55],[318,54],[317.29,54.29],[317,54.98],[316.28,56.14],[314.54,57.57],[311.94,58.62],[309.04,58.51],[306.89,57.69],[306,56.66],[306.59,55.46],[308,54.23],[309.41,52.91],[310,51.49],[309.72,50.44],[309.06,50],[308.21,50.29],[307.5,51],[306.22,51.71],[304.02,52],[301.54,51.82],[299.58,51.39],[298.46,50.66],[298,49.76],[298.75,48.33],[300.57,46.35],[302.44,43.91],[302.82,41.31],[302.3,39.34],[301.49,38.32],[300.48,38.74],[299.49,40.22],[297.82,42.07],[295,43.3],[291.5,44.18],[288,45.12],[284.41,45.52],[280.54,44.05],[276.18,42.52],[271.04,42.02],[265.94,42.56],[261.5,43.87],[257.75,45.94],[254.34,48.36],[251.08,51.79],[247.71,56.27],[244.75,61.43],[242.51,66.5],[241.27,72.05],[240.82,78.5],[241.34,85.78],[242.52,93.45],[244.53,100.94],[247.04,107.75],[248.94,112.66],[248.71,114.47],[246.71,114.93],[243.6,115.5],[239.99,116.76],[236.85,119],[234.62,122.06],[234.02,125.67],[234.3,128.81],[234.96,130.96],[236.11,131.62],[238.01,129.71],[240.18,127.79],[242.41,127],[244.18,127.18],[245.27,127.6],[245.23,129],[244.29,131.51],[243.38,134.36],[243.39,136.58],[244.07,138.35],[245.59,136.17],[247.38,134.63],[249.57,134],[252.02,134],[251.4,137.87],[251.25,140.83],[251.79,142.37],[252.82,141.93],[253.85,140],[255.01,137.88],[256.32,137],[258.27,137.19],[260.96,137.64],[263.94,139.05],[266.3,142.68],[268.43,147.07],[264.26,150.79],[261.22,154],[259.79,156.5],[259.99,157.99],[261.25,158.36],[262.49,158.68],[263,159.64],[262.19,160.95],[260.25,162.11],[257.35,162.9],[254.23,163.29],[251.46,162.9],[249.38,161.81],[248.05,159.7],[247.09,156.86],[246.38,153.5],[245.18,155.71],[244.15,158.55],[243.38,162.28],[243.34,166.37],[245.3,169.57],[247.83,172.5],[256.59,172.61],[264.2,173.08],[267.18,174.97],[268.46,177.22],[269,179.48],[268.36,181.88],[266.83,184.49],[264.33,186.84],[261.36,188.62],[257.25,189.59],[252,190],[246.72,189.58],[242.55,188.58],[239.72,186.9],[237.7,184.83],[236.33,181.72],[235.06,177.5],[233.54,172.79],[231.57,168.5],[229.04,163.29],[226.18,156.38],[223.12,147.31],[220.11,136.93],[217.15,125.61],[219.58,122.73],[221.38,119.66],[222,116.25],[221.58,113.25],[220.58,111.08],[219.37,109.17],[218.48,106.79],[217.8,104.07],[222.88,104.67],[227.07,104.81],[229.47,104.02],[230.37,102.88],[230.43,101.89],[229.2,101.26],[227.01,101],[224.62,100.49],[223.38,98.75],[222.92,95.55],[222.88,91.01],[223.16,87.04],[223.48,85.19],[224.61,85.41],[226.85,86.42],[229.12,87.31],[230.3,87.25],[230.41,85.04],[230.06,80.78],[229.4,75.06],[232.2,74.08],[234.52,72.63],[236.18,70.5],[236.86,67.75],[236.16,64.58],[235.5,61.76],[235.58,59.66],[235.41,57.75],[234.1,55.39],[232.62,52.87],[232,50.53],[232.7,48.56],[234.75,47.07],[237.5,45.82],[234.87,44.44],[232.83,42.99],[231.62,41.44],[231.34,40.03],[231.56,38.91],[232.38,38.27],[233.56,38],[234.58,37.81],[235,37.35],[233.97,35.64],[231.5,32.43],[228.97,28.6],[228,25.47],[227.65,23.46],[226.25,23.47],[224.72,24],[223.5,24.32],[222.75,24.01],[222.37,23],[222.23,21.5],[222.09,20],[221.56,19.4],[220.11,20.89],[218.47,22.4],[217.12,22.57],[216.46,21.82],[216.49,21.01],[216.57,19.66],[216.03,17.58],[215.06,15.76],[214.09,15],[213.32,15.81],[212.82,17.75],[212.5,20.5],[202.82,20.5],[193.14,20.5],[190.27,23.25],[187.2,25.37],[183.66,26],[179.97,26.44],[176.34,27.5],[172.74,29.01],[173.41,33.75],[173.94,37.69],[174.29,40.5],[173.89,42.11],[172,42.76],[169.5,42.56],[167,41.72],[165.08,40.06],[164.5,37.56],[163.93,35.08],[161.72,33.75],[159,33.37],[156.47,34.43],[154.55,36.45],[154,39.48],[154.54,42.53],[156.09,44.87],[158.55,46.43],[161.55,47.44],[165.43,47.69],[170.02,47.4],[175.11,46.73],[174.44,49.4],[173.92,51.79],[173.63,53.78],[174.65,55.14],[180,55.14],[186.5,54.77],[184.52,57.14],[182.71,59.17],[181.12,60.72],[178.32,61.51],[172.55,61.34],[166.18,61.21],[162.09,62.46],[158.77,64.17],[155.31,60.83],[150.69,55.62],[144.44,47.77],[137.04,38.05],[137.23,26.77],[136.97,16.63],[135.54,9.5],[133.93,4.89],[132.69,2.22],[131.57,1.51],[130.03,2.47],[128.5,3.55],[127.19,4],[125.9,3.27],[124.41,1.5],[122.96,-0.27],[121.78,-1]],[[194.19,25.23],[193.35,25.41],[193,26.42],[193.47,27.53],[194.58,28],[195.59,27.65],[195.77,26.81],[195.14,25.86],[194.19,25.23]],[[184.32,38.27],[183.17,38.58],[182.06,39.88],[181.55,41.48],[181.91,42.44],[183.03,42.66],[184.46,42.38],[185.53,41.48],[185.8,40.21],[185.24,39],[184.32,38.27]],[[181.36,44],[178.88,44.31],[177.46,45.07],[177.1,45.91],[177.15,46.44],[178.79,46.86],[182.25,47.43],[185.6,47.67],[187,47.26],[186.65,46.3],[185.8,45.2],[184,44.35],[181.36,44]]]: input);

input_width = points_array[0][0];
input_height= points_array[0][1];
sTrace = dispo_width/input_width;
stencil_height = input_height*sTrace + 2*margin;

difference() {

    //create token
    cylinder(h=token_thickness, d=token_width);

     //recess top
    translate([0,0,token_thickness-token_recess]){
        color("blue") cylinder(h=token_thickness+token_recess, d=token_width-token_rim_size);

    }

}

difference() {
     //token thickness (6) - token_recess (.5) = 5.5
     //stencil_thickness(22) - 5.5 = 16.5
     //16.5/2 = 8.25
     nt = token_thickness-token_recess;
	 translate([offX, offY,nt])

	scale([sTrace, -sTrace, 1])
	translate([-200, -150, 0]) {
		difference() {
			for (i = [1:len(points_array) -1] ) {
				linear_extrude(height=stencil_thickness) {polygon(points_array[i]);}
			}
		}
	}
}

```


[User]
I need to generate a set of single-sided tokens, and I'd like to be able to customize them. I'm thinking they should be circular and have some text on them, but I want to be able to change the text, obviously, as well as the size of the token, the thickness, and the size of the text itself. I also want to have a recessed area for the text to sit in, kind of like an engraving.

I envision this as a single main scad file that can create the tokens, with a few parameters at the top to control the dimensions, text, and other key aspects. Here's what I need it to do:

*   **Token Shape:** Circular
*   **Token Dimensions:**
    *   `token_width`: Diameter of the token.
    *   `token_rim_size`: Width of the rim (or the difference between the outer diameter and inner diameter of the recessed area).
    *   `token_thickness`: Total thickness of the token.
    *   `token_recess`: The depth of the recessed area for the text.
*   **Text:**
    *   `letter_text`: The text to be displayed on the token.
    *   `font`: The font used for the text.
    *   `letter_size`: The size of the text.
    *   `letter_height`: The thickness of the text (how far it extrudes).
*   **Text Placement:** The text should be centered within the recessed area.
*   **Color:** It would be nice if the recess and the text could be color coded, maybe the base token in the default material color, the recess in "blue" and the text in "red", just for easy differentiation in the preview.

I don't need anything fancy, just a solid base token, a recessed area, and some extruded text in that area. No need for libraries, just basic OpenSCAD features. I can work out specific values for the parameters. Could you write that for me?

[Assistant]
single_sided_text_token.scad:
```
//Customizable Double Sided Token

/*Token Variables*/
token_width = 40;
token_rim_size = 4;
token_thickness = 4;
token_recess = 1;

/*Text Varibles*/
font = "Impact";
letter_size = 15;
letter_height = 1;
letter_text = "text";

x=0;
y=0;

module void() {}

difference() {

    //create token
    cylinder(h=token_thickness, d=token_width);

     //recess top
    translate([0,0,token_thickness-token_recess]){
        color("blue") cylinder(h=token_thickness+token_recess, d=token_width-token_rim_size);

    }

}

union() {
    color("red")
    translate([x,y,(token_thickness-token_recess)]){
        linear_extrude(height = letter_height){
            rotate([0,0,90]) translate([0,0,5]) text(letter_text, font=font, valign="center", halign="center", size=letter_size);
        }
    }
}

```


[User]
I need a customizable Magic: The Gathering deck box designed in OpenSCAD. I'd like to be able to specify how many cards it should hold, with options for 30-40, 40-60, 50-80, or 60-100 cards. There should also be an option for the card size, either "with pro-sleeves" or "without pro-sleeves".

The key part is that I want to be able to apply a custom image to the side of the box. I have a program called "Stencil-o-Matic" that outputs coordinates that define the outline of the image. I need to be able to paste these coordinates into the OpenSCAD code, and then it should generate a stencil based on those coordinates that is extruded to create the image on the deck box. The stencil should also have adjustable size, position (left/right and up/down), and rotation. It looks like the current design is using the `Write.scad` file for its text; I'd also like to be able to specify font, size, and rotation for two lines of text above and below the image.

Finally, the basic box construction should have a separate lid and use the MCAD library. The code should also use the `build_plate.scad` utility for ensuring that it will fit on a build platform.

[Assistant]
uploads_aa_24_72_76_ff_MTGDeckBox1.scad:
```

/////////////////////////
//  Customizer Stuff  //
///////////////////////

// How many cards would you like to carry?
card_count = 40; //[30:40 Cards,40:60 Cards,50:80 Cards, 60:100 Cards]

card_type = 69; //[69:With Pro-Sleeves,65:Without Pro-Sleeves]

// Paste your Sencil-o-Matic code here or leave blank
input ="default_TNH";

image_size = 50; // [1:150]

Move_Image_Left_Right = 33; // [-50:50]

Move_Image_Up_Down = 45; // [10:50]

image_rotation = 0; // [0:359]

/////////////////////////
// Beat it Customizer //
///////////////////////

/* [Hidden] */

use <write/Write.scad>
use <MCAD/triangles.scad>
use <utils/build_plate.scad>

stencil_thickness =1.5 ;
offX = 3;
offY = 0;
margin = 10;
dispo_width = image_size;

points_array = (input=="default_TNH"? [[205,208],[[200.77,43.44],[199.19,44.3],[196.01,46.99],[191.68,50.99],[193.55,53.05],[194.78,55.02],[194.39,56.75],[193.34,58.01],[192.26,58.75],[191.3,58.4],[190.52,57.06],[189.37,55.5],[187.18,54.98],[184.97,54.76],[183.43,54.23],[182.43,53.13],[181.54,51.5],[180.72,49.5],[179.96,52],[179.39,54.88],[179.1,58.31],[179.3,60.89],[179.98,61.51],[181.26,61.4],[182.98,61.99],[184.42,63.43],[185,65.4],[184.53,67.2],[182.45,67.24],[180.21,67.31],[178.6,68.53],[176.8,69.81],[173.61,69.71],[169.92,69.11],[173.04,73.06],[175.78,75.84],[177.98,77],[179.42,76.56],[180.36,75.5],[181.23,74.44],[182.5,74],[183.76,74.42],[184.61,75.42],[184.5,77.17],[183.48,79.39],[182.43,81.56],[182.26,83.22],[181.67,84.61],[179.61,86.16],[176.5,87.82],[179.13,88.49],[181.82,88.86],[184.59,88.83],[186.91,88.01],[188,86],[189.01,84.07],[190.79,83.18],[192.57,83.43],[193,85.47],[193.48,88.04],[194.64,90.58],[195.74,93.09],[195.12,95.65],[193.95,98.21],[197.96,95.03],[200.97,92.5],[202.58,90.86],[202.51,89.6],[201.46,87.96],[200.3,86.11],[201.01,84.49],[202.35,83.45],[203.97,84.47],[205.96,85.55],[208.38,86],[210.8,86.66],[212.4,89.1],[213.57,91.25],[214.35,90.52],[214.81,88.07],[215,84.53],[214.57,80.85],[212.5,79.59],[210.58,78.56],[210,76.57],[210.41,74.77],[212.06,74.73],[214,74.59],[215.68,73.04],[217.6,71.37],[220.69,71.47],[224.13,72.12],[223.15,70.31],[221.83,68.37],[220.04,66.25],[218.13,64.66],[216.5,64],[215.16,64.62],[213.94,66.12],[212.8,68.23],[211.17,66.6],[210.09,64.9],[210.77,63],[211.64,60.92],[212,58.69],[212.77,56.54],[215.25,54.8],[218.5,53.23],[214.76,52.51],[211.38,52.18],[208.51,52.41],[206.51,53.53],[206,56.09],[205.63,58.44],[204.25,58.81],[202.87,57.78],[202.17,55.49],[201.38,52.78],[199.93,50.45],[198.03,48.4],[199.62,46.14],[200.61,44.41],[200.77,43.44]],[[197.72,59.03],[194.15,59.37],[191.5,60.16],[189.58,61.56],[187.77,63.38],[186.48,66.25],[186.02,70.53],[186.44,74.78],[187.56,77.78],[189.51,79.8],[192.01,81.51],[194.84,82.56],[197.58,83],[200.43,82.41],[203.53,80.98],[206.31,78.54],[208.44,75.49],[209.52,72],[209.44,68.51],[208.05,65.14],[205.36,62.01],[201.77,59.63],[197.72,59.03]],[[197.67,60.04],[194.61,60.51],[191.7,61.6],[189.45,63.4],[187.95,65.62],[187.28,68.39],[187,71.51],[187.61,74.68],[189.25,77.51],[191.96,79.6],[195.59,80.67],[199.42,80.68],[202.75,79.63],[205.33,77.53],[207,74.51],[207.62,71.16],[207.51,68.02],[206.16,65.18],[203.85,62.53],[200.8,60.68],[197.67,60.04]],[[117,105],[113.52,105.52],[111.04,107.19],[109.6,108.9],[109,109.94],[107.72,110.34],[104.65,110.5],[101.07,110.92],[98.57,111.94],[97.32,113.93],[97.18,117.14],[97.05,120.36],[95.35,121.89],[93.52,123.86],[93.17,129.69],[93.57,135.29],[94.75,138.25],[96.91,139.53],[100.1,140],[102.95,139.71],[104.5,139],[105.64,138.29],[107.5,138],[109.31,138.22],[110.32,138.75],[110.84,140.23],[111.46,142.75],[111.69,145.34],[110.83,146.42],[109.08,146.89],[106.75,147.44],[104.81,148.13],[104,148.95],[104.66,149.8],[106.25,150.55],[108.34,151.48],[110.21,152.74],[111.9,153.62],[113.54,153.62],[115.24,153.49],[117.07,154.04],[118.53,154.57],[119,154.05],[119.51,153.37],[120.75,153.17],[123.45,153.2],[127.5,153.08],[130.66,152.76],[131.25,152.34],[130.37,151.73],[130,150.99],[130.66,150.27],[132.25,149.75],[133.62,149.31],[131.5,148.64],[128.65,147.92],[126,147.19],[123.96,145.9],[123.24,142.98],[123.34,140.08],[123.95,138.01],[126.45,136.86],[133.04,136.63],[141.16,136.75],[142.22,134.41],[142.77,132.23],[142,130.5],[141.2,128.76],[142.02,126.45],[143.35,123.98],[140.82,121.24],[138.86,118.79],[137.68,116.75],[136.96,115.51],[136.1,115],[135.23,114.41],[134.5,112.99],[133.16,111.11],[130.84,109.42],[128.25,108.49],[126.26,108.45],[124.61,108.31],[122.81,107.03],[120.42,105.5],[117,105]],[[288.07,115.2],[287.5,108.61],[283.14,113.56],[278.81,118.81],[274.53,124.5],[271.2,129.74],[269.14,133.9],[268.34,137.59],[268,141.68],[268.43,145.81],[269.46,149.57],[271.43,152.57],[274.43,154.54],[278.35,155.57],[282.85,156],[287.4,155.55],[291.45,154.45],[294.75,152.31],[297.02,148.68],[298.35,144.41],[297.89,139.98],[295.98,134.8],[292.77,128.64],[289.34,121.84],[288.07,115.2]],[[123.96,123.47],[123.28,123.66],[123,124.94],[123.12,126.4],[123.39,127],[123.84,126.57],[124.35,125.53],[124.47,124.32],[123.96,123.47]],[[128.44,130.12],[127.26,130.33],[125.75,130.69],[124.51,131.28],[124,132.07],[124.22,132.72],[124.75,132.97],[125.85,132.51],[127.44,131.47],[128.54,130.47],[128.44,130.12]],[[109.79,131],[109.33,131.29],[109.5,131.99],[110.58,133.04],[112.31,134.14],[114.5,135.29],[112.6,133.15],[110.98,131.63],[109.79,131]],[[292.94,135],[292.09,135.92],[291.1,138.15],[290.5,140.89],[291.12,142.65],[292.3,143.6],[293.56,144],[294.66,143.45],[295.46,142.11],[295.54,140.02],[294.87,137.61],[293.81,135.77],[292.94,135]],[[244.5,198.09],[240.72,198.72],[235.86,200.13],[230.21,202.1],[231.3,204.98],[232.67,207.31],[234.44,208.87],[236.5,209.86],[234.45,209.93],[232.65,209.63],[231.2,208.8],[230.35,207.42],[230,205.8],[229.76,204.53],[229.19,204],[227.86,204.81],[225.8,206.75],[223.51,209.63],[221.61,212.7],[220.47,216.58],[220,221.45],[220.43,226.24],[222,229],[223.95,230.41],[225.85,231],[228.12,231.48],[230.98,232.64],[234.26,234.28],[233.67,240.65],[233.42,245.59],[233.64,248.51],[234.6,249.56],[236.11,250],[237.64,249.24],[238.1,246.25],[238.2,242.5],[239.25,245.75],[240.5,248.17],[242.14,249],[243.56,248.41],[244,246.5],[244.29,244.73],[245,244],[245.71,244.44],[246,245.5],[246.41,246.56],[247.39,247],[248.55,246.56],[249.36,245.5],[250.07,244.44],[250.97,244],[251.7,244.85],[252,246.89],[252.44,249.15],[253.58,250.39],[254.82,250.82],[255.58,251],[255.88,248.51],[256,242.51],[256,234.03],[261.95,232.27],[266.76,230.39],[268.95,228.21],[269.69,225.79],[269.99,223.2],[269.66,220.12],[268.88,216.5],[267.21,212.56],[264.76,208.7],[261.73,205.65],[258.62,205.49],[255.5,206.08],[257.82,204.72],[259.45,203.39],[258.71,202.17],[256.26,200.9],[252.39,199.51],[248.06,198.48],[244.5,198.09]],[[145.57,200.1],[142.67,200.39],[140,200.96],[138.4,201.69],[139,202.32],[141.09,202.99],[144,203.77],[146.41,204.65],[147.28,205.56],[146.38,206.32],[144.51,206.82],[142.88,207.32],[142.54,208.07],[144.93,208.73],[149.87,209],[155.35,209.37],[158.56,210.42],[159.71,211.64],[159.75,212.58],[158.47,212.76],[156.44,212.16],[153.88,210.99],[155.44,213.22],[156.54,215.34],[157,217.32],[157.59,218.79],[159,219.71],[160.53,220.79],[161,223.12],[160.68,225.15],[159.91,226],[158.98,225.42],[158.29,224.01],[157.67,222.77],[156.89,222.57],[156.26,222.49],[156,221.52],[155.6,220.46],[153.99,221],[150.6,221.92],[145.24,222.63],[139.22,223.49],[134.2,224.75],[130.73,226.46],[128.45,228.38],[127.43,230.98],[127,234.32],[127.49,238.08],[128.65,241.68],[130.95,244.86],[134.13,247.58],[138.65,249.54],[145.43,250],[151.39,249.73],[155.2,249.08],[157.41,248.23],[159.5,247.48],[160.1,246.61],[156.5,245.21],[151.5,243.64],[158.45,242.8],[164.03,241.77],[167.67,240.48],[169.59,238.82],[171.05,236.91],[171.73,235.34],[171.8,234.47],[170.39,234.48],[167.5,235],[164.57,235.49],[163.07,235.4],[163.57,234.34],[165.45,232.46],[167.67,230.72],[169,230],[169.82,229.25],[170.84,227.45],[171.66,224.94],[172,222.49],[171.52,219.37],[170.38,215.28],[168.63,211.17],[166.72,208],[165.24,206.4],[165.41,207.5],[166.07,209.79],[166.62,212.5],[167.11,215.5],[165.5,212],[163.58,208.71],[161.24,205.73],[157.9,203.33],[153.61,201.48],[149.2,200.46],[145.57,200.1]],[[136.71,204.06],[134.56,204.35],[132.5,204.95],[130.52,206.52],[128.56,209.11],[126.63,212.44],[129.45,210.22],[131.67,208.65],[133.04,208],[133.46,208.66],[133.4,210.25],[133.27,211.48],[133.56,211.28],[134.98,210.23],[137.57,209.42],[139.99,208.83],[141,208.32],[140.7,207.43],[139.96,205.93],[138.58,204.59],[136.71,204.06]],[[143.01,210.56],[140.52,209.71],[143.07,213.01],[145.5,215.58],[147.78,217.11],[149.8,217.34],[151.52,216.34],[152.54,214.74],[152.78,213.13],[151.7,211.88],[148.98,211.46],[145.79,211.18],[143.01,210.56]],[[232.26,217],[229.12,217.63],[226.45,219.65],[224.72,221.88],[224,223.52],[224.41,224.74],[225.41,225.93],[228.18,226.7],[233.16,226.8],[239.5,226.5],[239.81,223.84],[239.47,221.33],[237.87,219.09],[235.32,217.57],[232.26,217]],[[259.14,217],[255.74,217.54],[253.2,219.29],[251.6,221.63],[250.78,224.04],[250.5,226.5],[255.43,226.81],[259.6,226.86],[262.68,226.54],[264.47,225.51],[265,223.42],[264.7,221.05],[263.96,218.93],[262.29,217.45],[259.14,217]],[[155.81,225.05],[154.5,226.3],[151.77,229.24],[149.27,232.29],[148.48,233.81],[149.17,233.97],[150.21,233.75],[151.8,232.18],[153.81,229.13],[155.36,226.23],[155.81,225.05]],[[243.07,228.08],[242.22,228.1],[241.47,231.38],[241.08,234.8],[241.13,236.4],[242.35,236.8],[244.75,237.33],[248,237.95],[247.93,234.23],[247.63,231.01],[247,228.5],[246.3,227.63],[246.07,230.19],[245.73,232.98],[245,234.5],[244.28,234.14],[243.98,232.31],[243.71,229.91],[243.07,228.08]]]: input);

input_width = points_array[0][0];
input_height= points_array[0][1];
sTrace = dispo_width/input_width;
stencil_height = input_height*sTrace + 2*margin;

/////////////////////////
//   Moduletown USA   //
///////////////////////

module box_lid() {
	difference(){
		cube([card_type + 4,4 + card_count,18]);
		translate([1,1,10]) cube([card_type + 2,2 + card_count,18]);
		translate([2,2,2]) cube([card_type,card_count,18]);
	}
}

module box() {
	translate([4 + card_type,25 + card_count*3,0]) {

	rotate([0,0,180]){
	union(){
		translate([0,-10 + card_count,0]){
			difference() {
				translate([0,20,0]) cube([card_type + 4,4 + card_count,87]);
				scale(1.001) translate([card_type + 3.99,19.975,97]) rotate([0,180,0]) box_lid();
				translate([2,22,2]) cube([card_type,card_count,87]); // Inside
				translate([card_type/2-5,25,1]) #write("TNH",h=5,t=.5,font="write/orbitron.dxf");
			}
		}
	translate([card_type/2 + 2,9.8 + card_count,Move_Top_Text_Up_Down]) rotate([90,top_text_rotation,0]) write(top_text_label,h=top_text_size,t=1.5, font=top_text_font, center=true);
	translate([card_type/2 + 2,9.8 + card_count,Move_Bottom_Text_Up_Down]) rotate([90,bottom_text_rotation,0]) write(bottom_text_label,h=bottom_text_size,t=1.5, font=bottom_text_font, center=true);
	translate([Move_Image_Left_Right,9.8 + card_count+2,Move_Image_Up_Down]) rotate([90,image_rotation,0]) pattern();
	}
}
}
}

module pattern(){
	union(){
		translate([0, 0, stencil_thickness/2])
		translate([offX, offY, -stencil_thickness/2])
		scale([sTrace, -sTrace, 1])
		translate([-200, -150, 0]) {
			union() {
				for (i = [1:len(points_array) -1] ) {
					linear_extrude(height=stencil_thickness*2) {polygon(points_array[i]);}
				}
			}
		}
	}
}

//////////////////////////
// Put it all together //
////////////////////////

rotate([0,0,180]){ // Orient to fit build platform
		translate([-card_type/2,-card_count-7,0]){ // Attempt to center at origin
			box();
			scale([1.01,1.01,1.01]) box_lid();
	}
}

```


[User]
Hey, I'm looking for an OpenSCAD design for a customizable coaster. I want it to be made of a series of repeated shapes, sort of like a spirograph pattern.

Here are the parameters I'd like to control:

*   `num_sides`: The number of sides for each individual shape that gets repeated. For example, 8 would make octagons.
*   `num_shapes`: How many times that shape should be repeated around the center.
*   `coaster_diameter`: The overall diameter of the entire coaster.
*   `thickness`: The thickness of the walls of each shape.
*  `height`: How tall the coaster should be, this should be uniform
*   `center_hole_diameter`: The diameter of the hole in the center of the coaster.

I'd like the design to use `linear_extrude` to create the height, and use a `difference()` to subtract the inner circle, and center hole. The individual shapes should be created using a `circle(r=...)` and repeated using a `for()` loop, rotated and translated appropriately.

I'm not looking to add any special features like engravings or patterns, just a simple customizable spirocoaster.

[Assistant]
Spirocoaster.scad:
```
// Spirocoaster.scad
// Paddy Freer & Michael Barlow
// All units are in mm

num_sides = 8;          // Number of sides on each shape
num_shapes = 15;        // The number of times that each shape is duplicated
coaster_diameter = 80;  // This is the diameter of the entire shape
thickness = 2;          // The thickness of the walls
height = 2;             // Height of entire model
center_hole_diameter=8; // subtracted shape in the middle

outer_radius = coaster_diameter/4;
inner_radius = outer_radius - thickness;
center_hole_radius = center_hole_diameter/2;

$fn=num_sides;
linear_extrude(height)
difference() {
    for(i=[0:num_shapes]){
        position_angle=(i+1)*360/num_shapes;
        rotate([0, 0, 90-position_angle]) translate([0,outer_radius,0])
            difference(){
                circle(r=outer_radius);
                circle(r=inner_radius);
            }
    }
    circle(r=center_hole_radius);
}
```


[User]
Hey, I'm looking to generate a customizable soft robotic actuator for 3D printing, and I've got some basic dimensions in mind, but I want to be able to change them easily. It's based on a design I found on Thingiverse, thing 1096304. I've included a basic OpenSCAD file I hacked together that mostly gets the shape I want, but it's not great and definitely not parametric enough, plus I'm not sure if the way I made the mold is optimal.

The basic idea is there's a core shape, which is a slightly smaller cuboid, inside a mold.  The mold is also a cuboid, but it's bigger all around to allow for the soft material.  When the mold is printed and filled with soft material, it'll bend when inflated. I need to be able to control the length and width of the actuator, and I'd also like to be able to specify the wall thickness around the core.

So, here's what I'd like in the final design:
*   **Actuator Length:**  I need to be able to specify the overall length of the actuator.
*   **Actuator Width:**  I need to be able to set the overall width of the actuator.
*   **Wall Thickness:** A parameter to control the thickness of the material around the central core. This should apply to the sides, the top and the bottom of the core.
*   **Mold Clearance:** A parameter to control the clearance between the core and the mold. This should be a uniform distance, even on the ends.
*   **Mold Base Height:** An offset or parameter to increase the base of the mold, so that it can be printed well on its base and also to improve the mold's strength.

I'd like the OpenSCAD code to be parametric so I can easily change those values and have everything scale appropriately. The core should be centered within the mold in terms of width, length, and the "z" height should be offset so the bottom face of the core is above the base of the mold.

I'd also like to generate two separate STL files from the same source file - one for the core and one for the mold. This makes things easier for printing. The core will not be 3d printed, but rather serves as the negative for the mold.
Thanks!

[Assistant]
CustomActuator.scad:
```
length = 40;
width = 20;

moldHeight = length + 3;
moldWidth = width + 4;
moldLength = 7 + 4;

coreHeight = length - 3;
coreWidth = width - 5;
coreLength = 1 + 1;

union() {

translate([(moldWidth/2)-(coreWidth)/2,moldLength-5.5,3]) {
cube([coreWidth,coreLength,coreHeight]);
}

difference() {
cube([moldWidth,moldLength,moldHeight]);

translate([2,2,3]) {
cube([width,7,length]);
}
}
}
```


[User]
I need a parametric door stopper designed in OpenSCAD. I'd like to be able to control the overall length, width, and height of the stopper.

The basic shape should be a somewhat elongated triangular prism. The base of the prism should be on the XY plane, and the long edge of the prism should be aligned with the X axis, with the tip facing the positive X direction, and the short, flat base facing the negative X direction. The prism should extend up along the Z axis. The triangle's two angled edges should meet at the tip of the prism.

I want a rectangular cut-out in the top of the prism, essentially creating a smaller, secondary prism on top, that is centered along the width and length of the main prism, but shorter in length along the X axis. This top prism should also be shorter than the height of the main prism.

Additionally, I need a larger rectangular extrusion on top of this secondary prism, that is centered along the secondary prism's width and length axes, but also shorter than the secondary prism in terms of length, and shorter than the height of the secondary prism. I want this extrusion to extend out to the sides beyond the secondary prism in width. It can extend upwards to be greater than the secondary prism in height.

Finally, I need a rectangular cut through the main prism's angled surface, positioned along the length and width, towards the tip of the prism.

Can you provide an OpenSCAD file for this, using parameters for length, width, and height? I'm not using any external libraries for this.

[Assistant]
Doors_stoppers.scad:
```

//height
height = 25  ;
//length
length = 120 ;
//width
width= 60 ;

module toto() {
}

$fn = 24;

half_width  = width / 2;

ep_bor = 10;
bottom_cube_length = length - 4 * ep_bor;
bottom_cube_width = width - 2 * ep_bor;
bottom_cube_height = 6; // 3mm in / 3mm outsite
bottom_z = 0;
top_cube_length = length - 4 * ep_bor;
top_cube_width  = width - 2 * ep_bor;
top_cube_height = height;
top_z = height/2 + 7.5;
tip_cut_pos = length/2 - 0.5;
top_tip_pos = height + 0.5;

bloque_porte();

module bloque_porte()
{

    difference() {
	translate ([-length/2.0, 0, 0])
    	    polyhedron (
	    		points = [[0, -half_width, height], [0, half_width, height], [0, half_width, 0], [0, -half_width, 0], [length, -half_width, 0], [length, half_width, 0]],
			faces = [[0,3,2], [0,2,1], [3,0,4], [1,2,5], [0,5,4], [0,1,5],  [5,2,4], [4,2,3], ]);

	translate ([-1 * ep_bor, 0, top_z])
	     cube ([top_cube_length,
		     top_cube_width,
		     top_cube_height],
		    center=true) ;

	translate ([- length/2 + 10, 0, top_tip_pos])
	     cube ([length / 2,
		     width+2,
		     height / 2],
		    center=true) ;

	translate ([tip_cut_pos, 0, 0])
	     cube ([20,
		   width + 1,
		   height],
		  center=true) ;

    }
}
```


[User]
I'd like to design some Reuleaux shapes, specifically a 2D triangle, a 3D tetrahedron, and a solid of revolution based on the Reuleaux triangle, and I want to be able to control the size and other parameters.

For the 2D Reuleaux triangle, I need to be able to adjust the overall width, the thickness of the material, and the height of the extrusion. It should be a flat, planar object, like a keyring. The output should be a hollow shape.

The Reuleaux tetrahedron should let me set the overall size, and I'm happy with it being solid.

Finally, the solid of revolution should let me specify the overall width of the base Reuleaux triangle (which will determine the diameter of the final shape).

I would prefer that everything be parametric, meaning that I can specify the dimensions using variables at the top of the file. I'd like to use OpenSCAD.

Can you generate the SCAD files for these three objects? I think a single file is best since they're all related. I don't need to use any external libraries, just standard OpenSCAD functionality.

[Assistant]
Reuleaux_triangle.scad:
```
// Michael Barlow
// All measurements are in mm

object = 3; // [1:Reuleaux Triangle,2:Reuleaux Tetrahedron, 3:Reuleaux Solid of Revolution]
width = 40;
resolution = 200;
triangle_height = 2;
triangle_side_thickness = 4;

$fn = resolution;
radius = width;

if (object == 1){
    difference(){
        Reuleaux_triangle(radius);
        Reuleaux_triangle(radius - (triangle_side_thickness*2));
    }
} else if (object == 2) {
    Reuleaux_tetrahedron(radius);
} else {
    Solid_of_constant_width(radius);
}

module Reuleaux_triangle (rad = 100){
    linear_extrude(height=triangle_height)
        translate([0,-rad/2/sqrt(3),0]){
            intersection() {
                translate([rad/2,0,0]) {
                    circle(r = rad);
                }
                translate([-rad/2,0,0]) {
                    circle(r = rad);
                }
                translate([0,rad/2*sqrt(3),0]) {
                    circle(r = rad);
                }
        }
    }
}

module Solid_of_constant_width (rad = 100){
    rotate_extrude()
        difference(){
            translate([0,-rad/2/sqrt(3),0]){
                intersection() {
                    translate([rad/2,0,0]) {
                        circle(r = rad);
                    }
                    translate([-rad/2,0,0]) {
                        circle(r = rad);
                    }
                    translate([0,rad/2*sqrt(3),0]) {
                        circle(r = rad);
                    }
                }
            }
            translate([0,-radius,0]) {
                square(size = [2*radius, 2*radius]);
            }
        }
}

module Reuleaux_tetrahedron(rad = 100){
    translate([0,-rad/2/sqrt(3),-rad/2/sqrt(3)]){
        intersection() {
            translate([0,(rad/2)*sqrt(3),0]) {
                sphere(r=rad);
            }
            translate([0,(rad/2)/sqrt(3),(rad/2)*sqrt(3)]) {
                sphere(r=rad);
            }
            translate([-(rad/2),0,0]) {
                sphere(r=rad);
            }
            translate([rad/2,0,0]) {
                sphere(r=rad);
            }
        }
    }
}
```


[User]
Hey, I'm looking to design a parametric LM8UU linear bearing mount for a 3D printer carriage. I've seen some designs online, but I'd like to make one that's fully customizable, and I'd prefer to avoid relying heavily on minkowski operations, which seem computationally expensive.

The mount should basically consist of a base plate with a cylindrical cutout for the bearing, and also have mounting holes for attaching it to the carriage. I'd like to make it modular, so the baseplate and bearing holder should be able to vary independently.

Here are some parameters I'm thinking of:

*   **`holder_x`**: Overall width of the base plate (X-dimension).
*   **`holder_y`**: Overall depth of the base plate (Y-dimension).
*   **`holder_z`**: Height of the base plate (Z-dimension).
*   **`bearing_x`**: Width of the bearing holder area.
*   **`bearing_y`**: Depth of the bearing holder area.
*   **`bearing_z`**: Height of the top of the bearing holder relative to the bottom of the base plate.
*  **`bearing_outer_diameter`**: The diameter of the bearing itself (LM8UU is 15mm, but it should be parametric)
*  **`bearing_length`**: The length of the bearing itself (LM8UU is 24mm, but it should be parametric)
*   **`screw_size`**: Diameter of the mounting screws.
*   **`screw_x_base`**: The distance from the center to each of the mounting holes in the X direction.
*  **`screw_y_base`**: The distance from the center to each of the mounting holes in the Y direction.
*   **`screw_hole_depth`**: The depth of the hole for the screw (should extend all the way through the base)
*   **`screw_head_diameter`**: The diameter of the recessed area where the head of the mounting screw sits (should be slightly larger than `screw_size`).
*   **`screw_head_depth`**: The depth of the recess for the head of the screw.

I'd like the screw holes to be placed at the corners of a rectangle described by `screw_x_base` and `screw_y_base`, relative to the center of the mount, and to have a small counterbore. I would also like to have some small support "tie rings" on either side of the bearing to improve structural rigidity where the bearing sits. These tie rings should be circular profiles that are rotated and extruded to create the tie ring. The tie rings should be positioned at the `bearing_z` height.

I'm thinking we can accomplish this using linear_extrude on the tie rings and some cylinders for the mounting holes. The bearing holder can be a simple rectangular prism. I'd prefer to avoid difference operations on the main cube, if possible, to make the previews faster. I don't think any advanced libraries would be needed for this, so a simple OpenSCAD file should be sufficient.

[Assistant]
lm8uu_mount.scad:
```
//color("cyan")import ("YCarriagePart1v2.stl");

//screw parameters
Ssize = 4; // No decimal needed
xbase = 14;
ybase = 9.5;

//bearring holder
bearingx = 15;
bearingy = 25;
bearingz = 16;

//holder base
holderx = 35;
holdery = 25;

difference()
{
    union(){
        color("blue")minkowski(){ //base
        translate([0,0,0])cube([holderx,holdery,4],center=true);
                cylinder(h=1,r=2,$fn=50);

        }
        color("white")minkowski(){ //holder
            translate([0,0,bearingz-12+2])cube([bearingx,bearingy,8],center=true);
            cylinder(h=1,r=2,$fn=50);
        }
    }
        translate([0,0,-12])cube([50,50,20],center = true);

    translate([0,6,bearingz])tie_ring();
    translate([0,-6,bearingz])tie_ring();
    translate([0,0,bearingz])rotate([90,0,0])color("green")cylinder (h =24.25, d = 14.3,center = true);
    translate([0,0,bearingz])rotate([90,0,0])color("cyan")cylinder (h =30, d = 12,center = true,$fn=60);

    translate([xbase,ybase,-5])rotate([0,0,90])color("pink")cylinder (h =25, d = Ssize+0.5, $fn=60);
    translate([-xbase,ybase,-5])rotate([0,0,90])color("pink")cylinder (h =25, d = Ssize+0.5, $fn=60);
    translate([xbase,-ybase,-5])rotate([0,0,90])color("pink")cylinder (h =25, d = Ssize+0.5, $fn=60);
    translate([-xbase,-ybase,-5])rotate([0,0,90])color("pink")cylinder (h =25, d = Ssize+0.5, $fn=60);

    translate([xbase,ybase,0])rotate([0,0,90])color("pink")cylinder (h =5, r = Ssize+0.25, $fn=6);
    translate([-xbase,ybase,0])rotate([0,0,90])color("pink")cylinder (h =5, r = Ssize+0.25, $fn=6);
    translate([xbase,-ybase,0])rotate([0,0,90])color("pink")cylinder (h =5, r = Ssize+0.25, $fn=6);
    translate([-xbase,-ybase,0])rotate([0,0,90])color("pink")cylinder (h =5, r = Ssize+0.25, $fn=6);

}
module tie_ring(){
    rotate([90,0,0])rotate_extrude(){
    translate([9, 0, 0])square([2,4],true);
}
}
echo ((38.43-10.74)/2);
echo ((88.92-69.94)/2);
```