[User]
I'd like to generate a maze in the shape of a pyramid, similar to the one described in the provided file, but I'd like to have more control over the construction, and I'd like the design to be fully parametric. I'd like to define the core parameters:

*   **`side_length`**: the length of the base edge of the pyramid.
*   **`wall_thickness`**: the thickness of the maze walls.
*   **`wall_height`**: the height of the maze walls.
*   **`cblocks`**: the number of sectors in the maze at each level.
*   **`levels`**: the number of levels in the maze.
*  **`ring`**: a boolean that if true, will place a ring at the base of the pyramid.

The maze should be constructed within a pyramid shape, using a triangular grid for each of the four faces. Each triangular face will have a maze, where the walls are generated by a randomized maze algorithm. The maze should use a triangular grid, and each level should have `cblocks` sectors. The walls between these sectors are what make up the maze, and each level should start with `cblocks` blocks/sectors, and have less interior wall segments at the inner levels. The maze should be different each time it's generated.

For the pyramid itself, each face should be slightly tilted inward. I'd like each face of the pyramid to have an opening on the bottom two corners, to allow movement between faces. I'd also like there to be an optional ring around the bottom of the pyramid.

I'd like you to generate two SCAD files:
*   `maze_pyramid.scad`: This will contain the main code for generating the pyramid maze.
*   `maze_generation_lib.scad`: This will contain functions for generating a single triangular maze layer, and also the randomized maze logic for generating walls.

I don't require any external libraries such as MCAD, BOLTS, BOSL, or the like. I'd like to keep this pure OpenSCAD.

[Assistant]
maze_pyramid_generator.scad:
```
side_length = 50;
wall_thickness = 1;
wall_height = 2;

cblocks = 9;
levels = 5;

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;

/*
 * 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
            )
        )
    );

// give a [x, y] point and length. draw a line in the x direction
module x_line(point, length, thickness = 1) {
    offset = thickness / 2;
    translate([point[0] - offset, point[1] - offset, 0])
        square([length + thickness, thickness]);
}

// create a equilateral triangle
module equilateral_triangle(side_length, center = false) {
    radius = 0.57735026918962576450914878050196 * side_length;
	circle(r = radius, center = center, $fn = 3);
}

// create a holly equilateral triangle
module holly_equilateral_triangle(side_length, thickness = 1, center = true) {
    difference() {
	    offset(delta = thickness) equilateral_triangle(side_length, center = center);
	    equilateral_triangle(side_length, center = center);
    }
}

// Given a side length of a equilateral triangle, return the length of incenter to vertex.
function triangle_incenter_to_vertex_length(triangle_side_length) = 0.57735026918962576450914878050196 * triangle_side_length;

// used by `sector_cutter` module. Accepted angle is from 0 to 90 degree.
module a_quarter_sector_cutter(radius, angle) {
    outer = radius;
    polygon([[0, 0], [outer, 0], [outer, outer * sin(angle)], [outer * cos(angle), outer * sin(angle)]]);
}

// userd by `holly_equilateral_triangle_sector` for difference
module sector_cutter(radius, angles) {
    angle_from = angles[0];
    angle_to = angles[1];
    angle_difference = angle_to - angle_from;

    rotate(angle_from)
        if(angle_difference <= 90) {
            a_quarter_sector_cutter(radius, angle_difference);
        } else if(angle_difference > 90 && angle_difference <= 180) {
            sector_cutter(radius, [0, 90]);
            rotate(90) a_quarter_sector_cutter(radius, angle_difference - 90);
        } else if(angle_difference > 180 && angle_difference <= 270) {
            sector_cutter(radius, [0, 180]);
            rotate(180) a_quarter_sector_cutter(radius, angle_difference - 180);
        } else if(angle_difference > 270 && angle_difference <= 360) {
            sector_cutter(radius, [0, 270]);
            rotate(270) a_quarter_sector_cutter(radius, angle_difference - 270);
       }
}

// For a triangle, a different angle has a different length from incenter to its side.
function triangle_incenter_to_side_length(incenter_to_vertex_length, angle) =
    (angle >= 0 && angle <= 60) ? 0.5 * incenter_to_vertex_length / cos(angle) : (
	    (angle > 60 && angle <= 120) ? triangle_incenter_to_side_length(incenter_to_vertex_length, 120 - angle) : triangle_incenter_to_side_length(incenter_to_vertex_length, angle - 120)
	);

// draw a line from incenter to side for a equilateral triangle
module triangle_incenter_to_side_line(side_length, angle, incenter_to_side_blocks, level, thickness = 1) {
    block_length = triangle_incenter_to_side_length(triangle_incenter_to_vertex_length(side_length), angle + 60) / incenter_to_side_blocks ;

	rotate([0, 0, angle])
	    translate([block_length * (level - 1) + thickness, 0, 0])
		    x_line([0, 0], block_length - thickness / 2, thickness);
}

// draw a side between `angles[0]` and `angles[1]`
module holly_equilateral_triangle_sector(side_length, angles, thickness = 1) {
	intersection() {
        holly_equilateral_triangle(side_length, thickness);
	    sector_cutter(triangle_incenter_to_vertex_length(side_length) + thickness * 2, [angles[0], angles[1]]);
	}
}

module triangle_maze(side_length, cblocks, levels, thickness = 1) {
    full_circle_angle = 360;
    arc_angle = full_circle_angle / cblocks;
	incenter_to_side_blocks = levels - 2;

	divided_side_length = side_length / levels;

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

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

		angle = cc * arc_angle;

        if(v == 1 || v == 3) {
			triangle_incenter_to_side_line(side_length, angle, levels, cr + 2, thickness);
        }

        if(v == 2 || v == 3) {
		    sl = divided_side_length * (cr + 2);
			offset_angle = angle - arc_angle / 18;

			holly_equilateral_triangle_sector(sl, [offset_angle,  offset_angle + arc_angle], thickness);
        }

		if(v == 0 || v == 1) {
		    sl = divided_side_length * (cr + 2);
			factor = abs((arc_angle * (cc + 1)) % 120 - 60) / 60;
			offset_angle = angle - arc_angle / 18;

			holly_equilateral_triangle_sector(sl, [offset_angle, offset_angle + arc_angle * (sl - divided_side_length + factor) / (sl - factor * (cr + 2))], thickness);
		}
	}

	// inner triangle
	rotate([0, 0, 240])
		holly_equilateral_triangle_sector(divided_side_length, [-45, 315 - arc_angle], thickness);

	triangle_incenter_to_side_line(side_length, 300, levels, 2, thickness = 1);

	holly_equilateral_triangle_sector(divided_side_length * 2, [arc_angle / 4, 360], thickness);
}

// a maze triangle, but with two doors near two vertices
module triangle_maze_for_pyramid(side_length, cblocks, levels, wall_thickness = 1) {
	arc_angle = 360 / cblocks;
	divided_side_length = side_length / levels;

	offset_angle = arc_angle - arc_angle * (side_length - divided_side_length + 1) / (side_length - levels + 2);

	difference() {
		triangle_maze(side_length, cblocks, levels, wall_thickness);
		holly_equilateral_triangle_sector(side_length, [120, 120 + offset_angle + 0.5], wall_thickness);
		holly_equilateral_triangle_sector(side_length, [360 - offset_angle, 360], wall_thickness);
	}
}

// a maze triangle with two doors and tilted 54.5 degrees, for matching one side of a pyramid
module tilted_triangle_maze_for_pyramid(side_length, cblocks, levels, wall_height, wall_thickness = 1) {
	incenter_to_vertex_length = triangle_incenter_to_vertex_length(side_length);
	offset = incenter_to_vertex_length / 2 + wall_thickness;
	offset_angle = 54.5;

	translate([0, - side_length / 2 - wall_thickness * 1.6, 0])
	    rotate([offset_angle, 0, 0])
		    translate([0, offset , 0])
			    rotate([0, 0, 210])
				    linear_extrude(wall_height)
		                triangle_maze_for_pyramid(side_length, cblocks, levels, wall_thickness);
}

// tilted triangle mazes facing each other
module two_rotated_triangle_mazes_for_pyramid(side_length, cblocks, levels, wall_height, wall_thickness = 1) {
	tilted_triangle_maze_for_pyramid(side_length, cblocks, levels, wall_height, wall_thickness);
	mirror([0, 1, 0])
	    tilted_triangle_maze_for_pyramid(side_length, cblocks, levels, wall_height, wall_thickness);
}

module maze_pyramid(side_length, cblocks, levels, wall_height, wall_thickness = 1) {
	incenter_to_vertex_length = triangle_incenter_to_vertex_length(side_length);

	l = 0.78 * side_length;

	rotate([0, 0, 45])
	    cylinder(r1 = l, r2 = 0, h = l, $fn = 4);

	two_rotated_triangle_mazes_for_pyramid(side_length, cblocks, levels, wall_height, wall_thickness);

	rotate([0, 0, 90])
	    two_rotated_triangle_mazes_for_pyramid(side_length, cblocks, levels, wall_height, wall_thickness);
}

maze_pyramid(side_length, cblocks, levels, wall_height, wall_thickness);

if(ring == "YES") {
	translate([0, 0, 0]) rotate([90, 0, 0]) rotate_extrude(convexity = 10, $fn = 48)
		translate([side_length / 8, 0, 0])
			circle(r = side_length / 22, $fn = 48);
}
```


[User]
Hey, I'd like to make some customizable game tokens, and I think OpenSCAD is the best way to do it. I want to be able to specify the diameter of the coin, the number of facets it has (so I can have a circle or a polygon), its overall thickness, the text on the coin, the height of the text, and the font I use for the text. I need to be able to use stencil fonts so that the inner portions of the text don't fall out when I print it. I'm thinking I need to create a base coin shape, then cut out the text from it.

I'd like the default settings to be:

*   Coin diameter: 15 mm
*   Number of facets: 20
*   Coin thickness: 0.9 mm
*   Text: "10"
*   Text height: 8 mm
*   Font: "Allerta Stencil"

I also need the ability to use other stencil fonts like "Stardos Stencil" and "Sirin Stencil". I'll want to vary the text, diameter, thickness, and height frequently, so the code has to be parametric with a clear user interface.

Could you generate the OpenSCAD code for this? I'm not using any libraries, so please don't assume I have any of those installed.

[Assistant]
coin_customizer.scad:
```
/****************************
 * Coin Customizer          *
 * 1.0                      *
 * Jan 2016                 *
 * Stafford Tyo             *
 * Standards Electronics    *
 ****************************
 */

/* [Text] */

//Use only one or two characters, this is not meant for words (too long for a coin)
text=10;

//Height of the text, adjust as necessary to fit it on the coin
text_height=8;

//Stencil Font to use
font="Allerta Stencil"; //[Allerta Stencil,Stardos Stencil,Sirin Stencil]

/* [Coin] */

//Total coin diameter, in mm
coin_diameter=15; //[5:40]

//Number of sides to the coin, the more there are the smoother the circle
coin_facets=20; //[6:100]

//Overall thickness of the coin, best used with a multiple of your extrude height)
coin_thickness=0.9; //[0.2:0.1:4]

// preview[view:south, tilt:top]

//Written by Stafford Tyo, Standards Electronics 2016

a=coin_diameter;
b=coin_facets;
c=text_height;
d=coin_thickness;
e=(str(text));
f=font;

difference() {

cylinder (h=d, d=a, $fn=b);

linear_extrude(height=d*4, center=true, convexity=10) {
text (text=e, font=f, halign="center", valign="center", size=c);

}
}

```


[User]
Hey, I'd like to design a parametric spool holder for my 3D printer, and I want it to be easily customizable using OpenSCAD. I've seen a few designs out there, but I want to make something that fits my specific needs.

Here's what I'm thinking:

**Overall Structure:** The holder will consist of two mirrored side pieces connected by a bar. This should hold a standard filament spool.

**Side Pieces:**
*   I need to be able to adjust the height of the side pieces, which in turn will determine the overall width of the holder. The default height should be around 110mm.
*   There should be a slot at the top of each side piece to hold a rod or bar. I need to be able to specify the tightness of the fit between the slot and the bar, controlling this with a tolerance variable (default tolerance of 1mm).
*   I want the option to add screw feet to the bottom of the side pieces for bolting the holder to a surface. These should have holes for M8 bolts. If I don't use them, the feet should not be included in the model.
*   I would like the side pieces to be mostly flat on their sides, except for a semi-circular cut-out in the middle that I think would make the design look a little nicer.
*   I'd like to have a text label or logo on the side of the holder. I want to be able to change what it says, which font it uses, and its size.
*   To make the design a bit more efficient to print, I would like the design to be not a completely solid block of material on the side; I want to remove a small triangle of material that cuts out from the edges of the side piece.

**Customization:**
*   I want to be able to set the height of the holder with a variable.
*   I'd like to be able to set the tolerance between the bar and the slot using a variable.
*   I need a way to specify the text or character that goes on the side using a variable, including the font.
*   I'd like to be able to toggle the screw feet on and off with a variable (using "yes" or "no").
*   And finally, I need to be able to select from a variety of fonts that would be available in OpenSCAD. I'm happy with having "DejaVu Sans" as the default.

I think that's it! I'd really appreciate it if you could generate the OpenSCAD file(s) for me. I don't have any specific library requirements other than whatever is needed for basic text rendering, but do let me know if that impacts the file. It'd be great to have this design with all the parameters I mentioned. Thanks!

[Assistant]
Spoolholder-fablabmoebius-remix.scad:
```
// Configurator variables

// Height of the holder in mm.
holder_height = 110;

// How tight you want the bar to fit. in mm of airgap.
tolerance = 1.0;

// Letter or number to put on the side
character = "\u2318";

// Feet for bolting it to a surface
screw_feet = "no"; // [yes,no]

//DejaVu Sans was the font I found on this list with the most Unicode 7 implementations
font = "DejaVu Sans"; //[ABeeZee,Abel,Abril Fatface,Aclonica,Acme,Actor,Adamina,Advent Pro,Aguafina Script,Akronim,Aladin,Aldrich,Alef,Alegreya,Alegreya Sans,Alegreya Sans SC,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Alike,Alike Angular,Allan,Allerta,Allerta Stencil,Allura,Almendra,Almendra Display,Almendra SC,Amarante,Amaranth,Amatic SC,Amethysta,Amiri,Anaheim,Andada,Andada SC,Andika,Angkor,Annie Use Your Telescope,Anonymous Pro,Antic,Antic Didone,Antic Slab,Anton,Antonio,Arapey,Arbutus,Arbutus Slab,Architects Daughter,Archivo Black,Archivo Narrow,Arial,Arial Black,Arimo,Arizonia,Armata,Artifika,Arvo,Asap,Asset,Astloch,Asul,Atomic Age,Aubrey,Audiowide,Autour One,Average,Average Sans,Averia Gruesa Libre,Averia Libre,Averia Sans Libre,Averia Serif Libre,Bad Script,Balthazar,Bangers,Basic,Battambang,Baumans,Bayon,Belgrano,Belleza,BenchNine,Bentham,Berkshire Swash,Bevan,Bhavuka,Bigelow Rules,Bigshot One,Bilbo,Bilbo Swash Caps,Bitter,Black Ops One,Bokor,Bonbon,Boogaloo,Bowlby One,Bowlby One SC,Brawler,Bree Serif,Bruno Ace,Bruno Ace SC,Bubblegum Sans,Bubbler One,Buda,Buenard,Butcherman,Butcherman Caps,Butterfly Kids,Cabin,Cabin Condensed,Cabin Sketch,Caesar Dressing,Cagliostro,Calligraffitti,Cambay,Cambo,Candal,Cantarell,Cantata One,Cantora One,Capriola,Cardo,Carme,Carrois Gothic,Carrois Gothic SC,Carter One,Caudex,Cedarville Cursive,Ceviche One,Changa,Changa One,Chango,Chau Philomene One,Chela One,Chelsea Market,Chenla,Cherry Cream Soda,Cherry Swash,Chewy,Chicle,Chivo,Cinzel,Cinzel Decorative,Clara,Clicker Script,Coda,Coda Caption,Codystar,Combo,Comfortaa,Comic Sans MS,Coming Soon,Concert One,Condiment,Content,Contrail One,Convergence,Cookie,Copse,Corben,Courgette,Cousine,Coustard,Covered By Your Grace,Crafty Girls,Creepster,Creepster Caps,Crete Round,Crimson Text,Croissant One,Crushed,Cuprum,Cutive,Cutive Mono,Damion,Dancing Script,Dangrek,Dawning of a New Day,Days One,DejaVu Sans,Dekko,Delius,Delius Swash Caps,Delius Unicase,Della Respira,Denk One,Devonshire,Dhurjati,Dhyana,Didact Gothic,Dinah,Diplomata,Diplomata SC,Domine,Donegal One,Doppio One,Dorsa,Dosis,Droid Arabic Kufi,Droid Arabic Naskh,Droid Sans,Droid Sans Ethiopic,Droid Sans Mono,Droid Sans Thai,Droid Serif,Droid Serif Thai,Dr Sugiyama,Duru Sans,Dynalight,Eagle Lake,Eater,Eater Caps,EB Garamond,Economica,Eczar,Ek Mukta,Electrolize,Elsie,Elsie Swash Caps,Emblema One,Emilys Candy,Encode Sans,Encode Sans Compressed,Encode Sans Condensed,Encode Sans Narrow,Encode Sans Wide,Engagement,Englebert,Enriqueta,Erica One,Esteban,Euphoria Script,Ewert,Exo,Exo 2,Expletus Sans,Fanwood Text,Fascinate,Fascinate Inline,Faster One,Fasthand,Fauna One,Federant,Federo,Felipa,Fenix,Finger Paint,Fira Mono,Fira Sans,Fjalla One,Fjord One,Flamenco,Flavors,Fondamento,Fontdiner Swanky,Forum,Francois One,Freckle Face,Fredericka the Great,Fredoka One,Freehand,Fresca,Frijole,Fruktur,Fugaz One,Gabriela,Gafata,Galdeano,Galindo,Geneva,Gentium Basic,Gentium Book Basic,Geo,Geostar,Geostar Fill,Germania One,GFS Didot,GFS Neohellenic,Gidugu,Gilda Display,Give You Glory,Glass Antiqua,Glegoo,Gloria Hallelujah,Goblin One,Gochi Hand,Gorditas,Goudy Bookletter 1911,Graduate,Grand Hotel,Gravitas One,Great Vibes,Griffy,Gruppo,Gudea,Gurajada,Habibi,Halant,Hammersmith One,Hanalei,Hanalei Fill,Handlee,Hanuman,Happy Monkey,Headland One,Helvetica,Henny Penny,Hermeneus One,Herr Von Muellerhoff,Hind,Holtwood One SC,Homemade Apple,Homenaje,Iceberg,Iceland,IM Fell Double Pica,IM Fell Double Pica SC,IM Fell DW Pica,IM Fell DW Pica SC,IM Fell English,IM Fell English SC,IM Fell French Canon,IM Fell French Canon SC,IM Fell Great Primer,IM Fell Great Primer SC,Imprima,Inconsolata,Inder,Indie Flower,Inika,Inknut Antiqua,Irish Grover,Irish Growler,Istok Web,Italiana,Italianno,Jacques Francois,Jacques Francois Shadow,Jaldi,Jim Nightshade,Jockey One,Jolly Lodger,Josefin Sans,Josefin Sans Std Light,Josefin Slab,Joti One,Judson,Julee,Julius Sans One,Junge,Jura,Just Another Hand,Just Me Again Down Here,Kadwa,Kalam,Kameron,Kantumruy,Karla,Karla Tamil Inclined,Karla Tamil Upright,Karma,Kaushan Script,Kavoon,Kdam Thmor,Keania One,Kelly Slab,Kenia,Khand,Khmer,Khula,Kite One,Knewave,Kotta One,Koulen,Kranky,Kreon,Kristi,Krona One,La Belle Aurore,Laila,Lakki Reddy,Lancelot,Lateef,Lato,League Script,Leckerli One,Ledger,Lekton,Lemon,Lemon One,Liberation Sans ,Libre Baskerville,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Linden Hill,Lobster,Lobster Two,Lohit Bengali,Lohit Tamil,Londrina Outline,Londrina Shadow,Londrina Sketch,Londrina Solid,Lora,Loved by the King,Lovers Quarrel,Love Ya Like A Sister,Luckiest Guy,Lusitana,Lustria,Macondo,Macondo Swash Caps,Magra,Maiden Orange,Mako,Mallanna,Mandali,Marcellus,Marcellus SC,Marck Script,Margarine,Marko One,Marmelad,Martel Sans,Marvel,Mate,Mate SC,Maven Pro,McLaren,Meddon,MedievalSharp,Medula One,Megrim,Meie Script,Merge One,Merienda,Merienda One,Merriweather,Merriweather Sans,Mervale Script,Metal,Metal Mania,Metamorphous,Metrophobic,Miama,Michroma,Milonga,Miltonian,Miltonian Tattoo,Miniver,Miss Fajardose,Miss Saint Delafield,Modak,Modern Antiqua,Molengo,Molle,Monda,Monofett,Monoton,Monsieur La Doulaise,Montaga,Montez,Montserrat,Montserrat Alternates,Montserrat Subrayada,Moul,Moulpali,Mountains of Christmas,Mouse Memoirs,Mr Bedford,Mr Bedfort,Mr Dafoe,Mr De Haviland,Mrs Sheppards,Mrs Saint Delafield,Muli,Mystery Quest,Nanum Brush Script,Nanum Gothic,Nanum Gothic Coding,Nanum Myeongjo,Nanum Pen Script,NATS,Neucha,Neuton,New Rocker,News Cycle,Niconne,Nixie One,Nobile,Nokora,Norican,Nosifer,Nosifer Caps,Nothing You Could Do,Noticia Text,Noto Sans,Noto Sans Hebrew,Noto Sans Symbols,Noto Sans UI,Noto Serif,Nova Cut,Nova Flat,Nova Mono,Nova Oval,Nova Round,Nova Script,Nova Slim,Nova Square,NTR,Numans,Nunito,Odor Mean Chey,Offside,OFL Sorts Mill Goudy TT,Oldenburg,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Open Sans,Open Sans Condensed,Oranienbaum,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Overlock,Overlock SC,Over the Rainbow,Ovo,Oxygen,Oxygen Mono,Pacifico,Palanquin,Palanquin Dark,Paprika,Parisienne,Passero One,Passion One,Pathway Gothic One,Patrick Hand,Patrick Hand SC,Patua One,Paytone One,Pecita,Peddana,Peralta,Permanent Marker,Petit Formal Script,Petrona,Phetsarath,Philosopher,Piedra,Pinyon Script,Pirata One,Plaster,Play,Playball,Playfair Display,Playfair Display SC,Podkova,Poetsen One,Poiret One,Poller One,Poly,Pompiere,Ponnala,Pontano Sans,Poppins,Porter Sans Block,Port Lligat Sans,Port Lligat Slab,Prata,Preahvihear,Press Start 2P,Princess Sofia,Prociono,Prosto One,PT Mono,PT Sans,PT Sans Caption,PT Sans Narrow,PT Serif,PT Serif Caption,Puralecka Narrow,Puritan,Purple Purse,Quando,Quantico,Quattrocento,Quattrocento Sans,Questrial,Quicksand,Quintessential,Qwigley,Racing Sans One,Radley,Rajdhani,Raleway,Raleway Dots,Ramabhadra,Ramaraja,Rambla,Rammetto One,Ranchers,Rancho,Ranga,Rationale,Ravi Prakash,Redacted,Redacted Script,Redressed,Reenie Beanie,Revalia,Ribeye,Ribeye Marrow,Righteous,Risque,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rokkitt,Romanesco,Ropa Sans,Rosario,Rosarivo,Rouge Script,Rozha One,Rubik Mono One,Rubik One,Ruda,Rufina,Ruge Boogie,Ruluko,Rum Raisin,Ruslan Display,Russo One,Ruthie,Rye,Sacramento,Sail,Salsa,Sanchez,Sancreek,Sansation,Sansita One,Sarabun,Sarala,Sarina,Sarpanch,Satisfy,Scada,Scheherazade,Schoolbell,Seaweed Script,Sedan,Sedan SC,Sevillana,Seymour One,Shadows Into Light,Shadows Into Light Two,Shanti,Share,Share Tech,Share Tech Mono,Shojumaru,Short Stack,Siamreap,Siemreap,Sigmar One,Signika,Signika Negative,Simonetta,Sintony,Sirin Stencil,Six Caps,Skranji,Slabo 13px,Slabo 27px,Slackey,Smokum,Smythe,Sniglet,Snippet,Snowburst One,Sofadi One,Sofia,Sonsie One,Sorts Mill Goudy,Source Code Pro,Source Sans Pro,Source Serif Pro,Special Elite,Spicy Rice,Spinnaker,Spirax,Squada One,Sree Krushnadevaraya,Stalemate,Stalinist One,Stalin One,Stardos Stencil,Stint Ultra Condensed,Stint Ultra Expanded,Stoke,Strait,Strong,Sue Ellen Francisco,Sunshiney,Supermercado One,Suranna,Suravaram,Suwannaphum,Swanky and Moo Moo,Syncopate,Tahoma,Tangerine,Taprom,Tauri,Teko,Telex,Tenali Ramakrishna,Tenor Sans,Terminal Dosis,Terminal Dosis Light,Text Me One,Thabit,The Girl Next Door,Tienne,Tillana,Timmana,Tinos,Titan One,Titillium Web,Trade Winds,Trocchi,Trochut,Trykker,Tuffy,Tulpen One,Ubuntu,Ubuntu Condensed,Ubuntu Mono,Ultra,Uncial Antiqua,Underdog,Unica One,UnifrakturCook,UnifrakturMaguntia,Unkempt,Unlock,Unna,Vampiro One,Varela,Varela Round,Vast Shadow,Verdana,Vesper Libre,Vibur,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,VT323,Waiting for the Sunrise,Wallpoet,Walter Turncoat,Warnes,Wellfleet,Wendy One,Wire One,Yanone Kaffeesatz,Yellowtail,Yeseva One,Yesteryear,Zeyada]

// End of configurator variables

/* [Hidden] */
$fs = 1.0;
$fa = 1.0;

holder_width = holder_height;

module side(height, width, thickness, tolerances)
{
  bar_height = 10;
  bar_width = 8+tolerances;
  tab_width = 2-tolerances;
  tab_thickness = 2-tolerances;
  feet_height = 30;
  feet_holes_diameter = 5;
  feet_thickness = 8;
  feet_width = width/8;

  difference()
  {
    union()
    {
      cube([width/2, height, thickness]);
      if(screw_feet == "yes")
      {
        difference()
        {
          cube([feet_width, feet_thickness, feet_height]);
          translate([feet_width/2, 0, thickness+(feet_height-thickness)/2])
            rotate([270,0,0])
              cylinder(d=feet_holes_diameter, h=feet_thickness+2);
        }
      }
    }
    translate([width/2 - bar_width/2, height-bar_height, tab_thickness])
      cube([bar_width, bar_height, thickness]);
    translate([width/2 - (bar_width-tab_width*2)/2, height-bar_height, 0])
      cube([bar_width-tab_width*2, bar_height, thickness]);
    translate([width/2,0-width/3,0])
      cylinder(d=width, h=thickness);

    polyhedron(
      points=[ [0,0,0],[width/2 - bar_width/2 -5, height, 0],[0, height, 0],
               [0,0,feet_height],[width/2 - bar_width/2-5, height, feet_height],[0, height, feet_height] ],
      faces=[ [0,2,1],[0,1,3],[1,4,3],[1,2,4],[2,5,4],[2,0,5],[0,3,5],[3,4,5] ]
    );

    translate([width/2,height/2-height*0.05,0])
      cylinder(d=height/3, h=thickness);
  }
}
render()
{
  difference()
  {
    union()
    {
      side(holder_height,holder_width, 10, tolerance);
      translate([holder_width, 0, 0]) mirror([1,0,0]) side(holder_height,holder_width, 10, tolerance);
    }
    translate([holder_width/2, holder_height-holder_height/2/2, 8])
      linear_extrude(height=2) text(character, font=font, valign="center", halign="center", size = holder_height/7);
    //cube([120, 70, 10]);
  }
}

```


[User]
I need to create a bar for a spool holder, and I'd like it to be parametric so I can easily adjust the length. I'm envisioning a rectangular bar with a cylindrical cutout running its length, like a tunnel. I want to be able to specify the overall length of the bar, the width and height of the rectangular bar and the diameter of the cylindrical cut-out. Additionally I need to make sure that the corners of the rectangle are removed from the full rectangular block.

To clarify, the overall shape will be a rectangular bar with a cylindrical void running through it along its length. The bar will have rectangular corner cutouts.

Could you generate the OpenSCAD code for this? I think I need to have the length of the bar, the width and height of the bar, and the diameter of the cylinder to be configurable. Also, I need to control the dimensions of the corner cut-outs, and the spacing of those cutouts from the bar ends. Finally, the cutouts should go all the way through the height plus the diameter of the cut cylinder.

[Assistant]
bar-Spoolholder-fablabmoebius-remix.scad:
```
// Configurator variables

// length of the bar in mm.
bar_length = 110;

// End of configurator variables

/* [Hidden] */

bar_height = 10;
bar_width = 8;
difference()
{
  union()
  {
    cube([bar_length, bar_width, bar_height]);
    translate([0, bar_width/2, bar_height]) rotate([0,90,0]) cylinder(d=bar_width, h=bar_length);
  }
  translate([8,0,0])
    cube([2,2,bar_height+bar_width]);
  translate([bar_length-10,0,0])
    cube([2,2,bar_height+bar_width]);
  translate([8,bar_width-2,0])
    cube([2,2,bar_height+bar_width]);
  translate([bar_length-10,bar_width-2,0])
    cube([2,2,bar_height+bar_width]);
}
```


[User]
Okay, I'm looking to create a parametric L-bracket generator in OpenSCAD for a robotics platform called MakeBlock. MakeBlock uses a consistent 8mm pitch for all of their structural components, so it's important that the holes in the bracket align with that. They also use M4 screws, so the holes should either be slightly smaller (around 2mm radius) for the screw to self-tap or slightly larger (around 2.4mm) for a loose fit where a nut is needed. The bracket needs to be L-shaped, with a specified number of holes along each leg and the Z axis for the depth of the bracket. The part will print on its side, so it should account for that.

Here are the specific design requirements:

*   **Parametric L-Shape:** The script should generate an L-bracket with configurable hole counts along each axis (x, y, and z). The 'xholes' and 'yholes' parameters will determine the number of holes on each leg of the "L" and 'zholes' parameter will determine the number of holes in the z axis, all will be configurable.
*   **MakeBlock Pitch:** Hole centers must be spaced 8mm apart in each axis and all holes must be aligned to this grid.
*   **Thickness:** The thickness of the bracket should be a configurable parameter ('thickness'). Default should be 3mm.
*   **Hole Radius:** The radius of the holes should be configurable ('hole_radius'). The user should be able to set the hole radius to around 2.0mm for a self-tapping screw fit or around 2.4mm for a looser, nut-based fit.
*   **Hole Sides:**  The script should allow users to choose the number of sides for the holes to allow for accurate printing. There should be an option for circles, hexes, and octagons; and a more general selection to allow for any number of sides.
*   **Corner Radius:** The inside corners of the L-bracket should be rounded, with the corner radius being configurable ('corner_radius'). Default should be 2mm.
*   **Orientation:** The part is intended to be printed on its side, so the script should be oriented that way or should be easily rotatable to facilitate it.
*   **Printing:** The parts will be 3D printed, so keep extrusion printing limitations in mind, particularly concerning overhangs and the tendency for circular or near-circular holes to close up a bit.

I'd like the ability to adjust all of these parameters in the customizer. I don't think I'll need any external libraries like MCAD, BOSL, BOSL2, etc. for this design. It should all be relatively straightforward.

Please create an OpenSCAD file that implements these requirements. Let me know if you need clarification.

[Assistant]
MakeBlockEl_v4.scad:
```
/* MakeBlock L-Bracket by Sam Kass 2016 */

// # holes in x direction on one side of L
xholes=4;

// # holes in y direction on other side of L
yholes=3;

// # holes in z direction (wide)
zholes=3;

// part thickness (MakeBlock=3)
thickness=3;

// radius of holes (MakeBlock threaded=2, MakeBlock loose=2.4)
hole_radius=2.4;

// # sides for holes (0 = circle, printers often prefer 6 or 8)
hole_sides=8;

// radius of part corners (MakeBlock=2)
corner_radius=2;

// distance from the center of one hole to center of next (MakeBlock=8)
pitch=8;

/* [Hidden] */
$fn=50;
open=[];

d=0.01;
cr = corner_radius < 0.001 ? 0.001 : corner_radius;

module template(nholes) {
    hull() {
        translate([0, -(pitch/2), 0])
            cube([2*cr,2*cr,thickness]);

        translate([0, (zholes-1)*pitch+(pitch/2-2*cr), 0])
            cube([2*cr,2*cr,thickness]);

        translate([(nholes)*pitch+(pitch/2-cr), -(pitch/2-cr), 0])
            hex(cr*2,0.1,thickness);
//            cylinder(h=thickness, r=cr);

        translate([(nholes)*pitch+(pitch/2-cr), (zholes-1)*pitch+(pitch/2-cr), 0])
            hex(cr*2,0.1,thickness);
//            cylinder(h=thickness, r=cr);
    }
}

// Get vertex distance for desired side-to-side width (apothem).
function poly_r_for_d(d,sides) = d/cos(180/sides);

// angle to polygon vertex.  Add sides/4 and 180/sides to make flat on top.
function vert_ang(s,sides) = (s + sides/4)*360/sides+180/sides;

module poly(wid,rad,height,sides){
    hull(){
        for (s = [0:sides]) {
            rotate([0,0,vert_ang(s,sides)])
                translate([poly_r_for_d(wid/2,sides)-rad,0,0])cylinder(r=rad,h=height);
        }
    }
}

// Unroll for loop for hex so it's not so slow in common case
module hex(wid,rad,height){
    hull(){
        rotate([0,0,vert_ang(0,6)])
            translate([poly_r_for_d(wid/2,6)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(1,6)])
            translate([poly_r_for_d(wid/2,6)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(2,6)])
            translate([poly_r_for_d(wid/2,6)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(3,6)])
            translate([poly_r_for_d(wid/2,6)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(4,6)])
            translate([poly_r_for_d(wid/2,6)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(5,6)])
            translate([poly_r_for_d(wid/2,6)-rad,0,0])cylinder(r=rad,h=height);
    }
}

// Unroll for loop for oct so it's not so slow in common case
module oct(wid,rad,height){
    hull(){
        rotate([0,0,vert_ang(0,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(1,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(2,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(3,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(4,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(5,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(6,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
        rotate([0,0,vert_ang(7,8)])
            translate([poly_r_for_d(wid/2,8)-rad,0,0])cylinder(r=rad,h=height);
    }
}

module hole() {
    if (hole_sides == 0) {
        cylinder(h=thickness+2*d, r=hole_radius);
    }
    else if (hole_sides == 6) {
        hex(hole_radius*2,0.1,thickness+2*d);
    }
    else if (hole_sides == 8) {
        oct(hole_radius*2,0.1,thickness+2*d);
    }
    else {
        poly(hole_radius*2,0.1,thickness+2*d, hole_sides);
    }
}

module holes(nholes) {
    for (y = [0:zholes-1]) {
        for (x = [1:nholes]) {
            translate([x*pitch,y*pitch,-d])
            hole();
        }
    }

}

rotate([90,0,0])
translate([-0, pitch/2, 0])
union() {
    translate([-thickness,0,0])
    difference() {
        template(xholes);
        holes(xholes);
    }

    rotate([0,-90,0])
    difference() {
        template(yholes);
        holes(yholes);
    }
}

```


[User]
Hey, I'm looking to create some OpenForge-compatible terrain tiles, but with a few specific tweaks for my needs. I'd like to use a thin base design with magnetic connectors and support for paperclip reinforcement. I've seen some existing designs but want something parametric, so I can customize the size. I'm a little new to this so I want to ask for help.

Here are the core features I'm looking for:

*   **Parametric Tile Size:** I want to be able to define the base tile size using `x` and `y` parameters, representing the number of 25mm squares along each dimension, similar to the code I provided. So, a `x=2` and `y=3` would make a 2x3 tile.
*   **Thin Base:** The tile should have a thin base with a thickness of approximately 6mm.
*   **Edge Support:** I need to be able to optionally add edge supports of a defined `edge` size, say 12.5mm, to any of the four sides (left, right, front, back), controlled by boolean parameters. These edge supports need to be flush with the existing sides.
*   **Magnet Holes:** I want to include holes for 5mm buckyball magnets. The size should be adjustable via a `magnet_hole` parameter (e.g. 5.4mm). These holes should be placed on the top surface along the edges at the midpoint of each side for each 25mm grid square and corners, meaning that the number of magnets scales with x and y. They should only go partially through the base.
*   **Wire Holes:** I'd like to have holes in the base for paperclip reinforcement. These need to be controlled by two parameters, `center_wire_hole`, for holes in the center of each grid cell, and `paperclip_wire_hole` for holes for paperclip loops around these center points. For the paperclips, I'd like four loops around each center hole and additional pairs to each side of the center point going along the long axis of the tile. If either wire hole parameter is set to 0, those holes should be omitted.
* **Magnet Ejection Holes**: I need small magnet ejection holes on the underside of the tile, to push the magnets out if needed, aligned with the center of each magnet hole, but on the bottom face.

I think I need at least a couple of modules, like `fs_magnet_holder`, and  `fs_magnet_base` that I can call at the base level using the parameters. And another module called `wire_holes` and `magnet_ejection_holes`.

I'm hoping that you can generate the OpenSCAD code for these, I want to make sure it's parametric enough for me to tweak in the future. I don't need any additional libraries other than the default OpenSCAD library.

[Assistant]
filament_saver_magnetic.scad:
```
/* [Base Tile Size] */
x = 2; //[1,2,3,4,5,6]
y = 2; //[1,2,3,4,5,6]

/* [Edge Tile Support] */
// Size of edge tile addition, 9.2 is standard for openforge
edge = 12.5;
left_edge = "false"; // [false,true]
right_edge = "false"; // [false,true]
front_edge = "false"; // [false,true]
back_edge = "false"; // [false,true]

/* [Magnets] */
// Size of hole for magnet.  5.4 works well for 5mm buckyball style magnets.  0 to eliminate.
magnet_hole = 5.4;

/* [Wire Holes] */
// Size of hole for center wire hole.
center_wire_hole = 0; // 0:Off,0.65:20 Gauge,0.7:18 Gauge
paperclip_wire_hole = .7; // 0:Off,0.65:20 Gauge,0.7:18 Gauge

module wire_holes(x,y,center_wire_hole=0,paperclip_wire_hole=0.7,edge=12.5) {
    // center: 1  mm wire  18 gauge
    // crossed: .8  mm wire  20 gauge 7.4 small  9.3 large  20 gauge
    eoffset = -1 - edge;

    xlength = 2 + 2 * edge + 25 * x;
    if (x > 1) {
        for ( i = [2 : x] ) {
            ipos = i * 25 - 25;
            translate([ipos,eoffset,6/2]) rotate([-90,0,0]) cylinder(xlength,center_wire_hole,center_wire_hole,$fn=50);
            // Large Loop
            translate([ipos+4.5,eoffset,6/2+1.5]) rotate([-90,0,0]) cylinder(xlength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([ipos-4.5,eoffset,6/2+1.5]) rotate([-90,0,0]) cylinder(xlength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([ipos+4.5,eoffset,6/2-1]) rotate([-90,0,0]) cylinder(xlength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([ipos-4.5,eoffset,6/2-1]) rotate([-90,0,0]) cylinder(xlength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            // Small Loop
            translate([ipos+2.4,eoffset,6/2+1.5]) rotate([-90,0,0]) cylinder(xlength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([ipos-2.4,eoffset,6/2+1.5]) rotate([-90,0,0]) cylinder(xlength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
        }
    }

    ylength = 2 + 2 * edge + 25 * y;
    if (y > 1) {
        for ( i = [2 : y] ) {
            ipos = i*25-25;
            translate([eoffset,i*25-25,6/2]) rotate([0,90,0]) cylinder(ylength,center_wire_hole,center_wire_hole,$fn=50);
            // Large Loop
            translate([eoffset,ipos+4.5,6/2+1.5]) rotate([0,90,0]) cylinder(ylength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([eoffset,ipos-4.5,6/2+1.5]) rotate([0,90,0]) cylinder(ylength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([eoffset,ipos+4.5,6/2-1]) rotate([0,90,0]) cylinder(ylength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([eoffset,ipos-4.5,6/2-1]) rotate([0,90,0]) cylinder(ylength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            // Small Loop
            translate([eoffset,ipos+2.4,6/2+1.5]) rotate([0,90,0]) cylinder(ylength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
            translate([eoffset,ipos-2.4,6/2+1.5]) rotate([0,90,0]) cylinder(ylength,paperclip_wire_hole,paperclip_wire_hole,$fn=50);
        }
    }
}

module magnet_ejection_holes(x,y,magnet_hole,
        left_edge="false",right_edge="false",front_edge="false",back_edge="false",edge=12.5) {
    if (magnet_hole > 0) {
        l = left_edge == "true"?edge:0;
        r = right_edge == "true"?edge:0;
        f = front_edge == "true"?edge:0;
        b = back_edge == "true"?edge:0;
        for ( i = [1 : x] ) {
            translate([i*25-12.5,3.6-f,-1]) cylinder(10,.9,.9,$fn=50);
            translate([i*25-12.5,y*25-3.6+b,-1]) cylinder(10,.9,.9,$fn=50);
        }
        for ( i = [1 : y] ) {
            translate([3.6-l,i*25-12.5,-1]) cylinder(10,.9,.9,$fn=50);
            translate([x*25-3.6+r,i*25-12.5,-1]) cylinder(10,.9,.9,$fn=50);
        }
    }
}

module fs_magnet_holder(magnet_hole=5.4) {
    // 5mm buckyballs
    if(magnet_hole > 0) {
        difference() {
            cylinder(6,3.7,3.7, $fn=100);
            translate([0,0,-1]) cylinder(8,magnet_hole/2,magnet_hole/2, $fn=100);
        }
    }
}

module fs_magnet_base(x,y,
        left_edge="false",right_edge="false",front_edge="false",back_edge="false",edge=12.5,
        magnet_hole=5.4,center_wire_hole=0,paperclip_wire_hole=0.7) {
    l = left_edge == "true"?edge:0;
    r = right_edge == "true"?edge:0;
    f = front_edge == "true"?edge:0;
    b = back_edge == "true"?edge:0;
    difference() {
        union() {
            translate([-l,-f,0]) difference() {
                cube([25*x+l+r,25*y+f+b,6]);
                translate([1,1,-1]) cube([25*x-2+l+r,25*y-2+f+b,8]);
            }
            translate([6.4-l,6.4-f,0]) difference() {
                cube([25*x-12.8+l+r,25*y-12.8+f+b,6]);
                translate([1,1,-1]) cube([25*x-14.8+l+r,25*y-14.8+f+b,8]);
            }
            translate([-l,-f,0]) difference() {
                cube([25*x+l+r,25*y+f+b,1]);
                translate([7,7,-1]) cube([25*x-14+l+r,25*y-14+f+b,8]);
            }
            for ( i = [1 : x] ) {
                translate([i*25-12.5,3.7-f,0]) fs_magnet_holder(magnet_hole);
                translate([i*25-12.5,y*25-3.7+b,0]) fs_magnet_holder(magnet_hole);
            }
            for ( i = [1 : y] ) {
                translate([3.7-l,i*25-12.5,0]) fs_magnet_holder(magnet_hole);
                translate([x*25-3.7+r,i*25-12.5,0]) fs_magnet_holder(magnet_hole);
            }
            if (x > 1) {
                for ( i = [2 : x] ) {
                    translate([i*25-25-6,-f,0]) cube([12,7,6]);
                    translate([i*25-25-6,y*25-7+b,0]) cube([12,7,6]);
                }
            }
            if (y > 1) {
                for ( i = [2 : y] ) {
                    translate([-l,i*25-25-6,0]) cube([7,12,6]);
                    translate([x*25-7+r,i*25-25-6,0]) cube([7,12,6]);
                }
            }
            translate([-l,-f,0]) cube([7,7,6]);
            translate([x*25-7+r,-f,0]) cube([7,7,6]);
            translate([-l,y*25-7+b,0]) cube([7,7,6]);
            translate([x*25-7+r,y*25-7+b,0])translate([]) cube([7,7,6]);
        }
        wire_holes(x,y);
        magnet_ejection_holes(x,y,magnet_hole,
                left_edge=left_edge,right_edge=right_edge,front_edge=front_edge,back_edge=back_edge,edge=12.5);
    }
}

fs_magnet_base(x,y,left_edge,right_edge,front_edge,back_edge,edge,magnet_hole,center_wire_hole,paperclip_wire_hole);

```


[User]
I'm looking to create a parametric OpenSCAD design for a wasteboard surfacing block. I have a simple example that just makes a rectangular prism, but I'd like it to be more flexible.

Specifically, I need the following parameters:

*   `X`:  The length of the block in the X dimension.
*   `Y`: The length of the block in the Y dimension.
*   `Z`: The height (thickness) of the block.
*   `chamfer`: A parameter to add a chamfer along the edges of the block (all edges should be chamfered). This could be 0 if no chamfer is wanted.

I'd like to be able to control all of these values via customizer variables.

Could you generate the necessary OpenSCAD file that implements this?

[Assistant]
waste_board_surface_block.scad:
```
//CUSTOMIZER VARIABLES

//1. Box X Length
X = 500;

//2. Box Y Length
Y = 700;

//3. HBox Z Height
Z = 1;

//CUSTOMIZER VARIABLES END

cube([X,Y,Z]);
```


[User]
Hi, I need a parametric OpenSCAD design for an air vent adapter. I have a portable AC unit and need to connect its exhaust pipe to a window, but the window sill is too deep, and I need a custom adapter.

Here's the idea. The adapter should take a circular pipe opening and transition it to a rectangular slot that fits in a window. I need it to be fully parametric so I can adjust it for different pipe sizes and window configurations.

Here are the key parameters I'd like to be able to control:

*   **`windowOpeningDistance`**: The width of the rectangular slot that fits into the window. This would be the width of a piece of wood used to fill the window opening, minus any inset.
*   **`openingDepth`**: The depth of the rectangular slot – how far back from the window surface that the adapter starts.  This determines how far away it starts transitioning to the pipe connection and prevents it interfering with window ledges and such.
*   **`t`**: The thickness of the walls of the adapter.
*   **`pipeDiameter`**: The diameter of the circular pipe that will connect to the adapter.
*   **`pipeOffset`**: How far to offset the center of the pipe relative to the center of the rectangular slot. This will allow the pipe to clear any obstructions like sills.
*   **`taperLength`**: The length of the transition from the rectangular slot to the circular pipe.
*   **`pipeConnectLength`**: The length of the pipe connection portion of the adapter.
*   **`lip`**: The thickness of the lip around the slot for screw holes to secure to a wood backing.
*   **`screwHoleDia`**: The diameter of the screw holes used to secure it all.

The design should ensure that the cross-sectional area of the rectangular slot at its narrowest point is roughly equal to the cross-sectional area of the circular pipe, so there is no bottleneck in airflow. To achieve this it will need to compute a `y` value for the hight of the rectangle based on the x (width from `windowOpeningDistance`) and `pipeDiameter` variables. The y is derived by assuming the circular section of pipe is halfed vertically and a rectangle inserted so that its area would equal the area of the circle.

The design will use 2 circles as its base and then hull a rectangular section in between them and then extrude to achieve this shape. then subtract from this wall thickness (`t`) all around to achieve the correct shape. Then a transition section using a similar technique, transitioning from 2 circles to a single larger circle (or vice-versa) and then finally a pipe section that extends off that. And finally an extruded base section which is slightly wider with screw holes.

The adapter will need to have:
    - a rectangular slot section with screw lips for attaching to a board for installation in a window.
    - A section that tapers from the rectangular slot to a circular pipe opening.
    - The circular pipe connection section to connect to the exhaust hose

The design would need to:
    - compute the height (`y`) of the rectangle based on ensuring the cross sectional area matches that of the output pipe
    - Use circles and hulling techniques to achieve the rectangular section with rounded sides
    - Use similar techniques to achieve the transition from the rectangle to a circle
    - Create holes for screws in the lip sections

I'm not using any special libraries, so it should all be fairly basic primitives and transforms. Could you generate the OpenSCAD code for this?

[Assistant]
airvent.scad:
```
//how wide you want window to be open i.e the width of your bit of wood
windowOpeningDistance=80;
//at what depth can we start transitioning into pipe connection
openingDepth=50; //min/max: [10:100]
//thickness of walls
t=1;
//umm the diameter of your pipe...
pipeDiameter=100;
//offest of pipe opening based on window opening
pipeOffset=15;	//min/max: [0:100]
//how long to take to change from pipe to window slot shape
taperLength=50;	//min/max: [10:100]
//length that pipe attches to
pipeConnectLength=20;
//set this to be a bit bigger than the diameter of the head of your screws
lip=5;
//diameter of holes for screws
screwHoleDia=2;

z=openingDepth;
x=windowOpeningDistance-(2*lip);
y=(PI/4)*((pow(pipeDiameter,2)/x)-x); //height it must be based on x to = surface area of pipe opening i.e no air flow restriction

union(){
translate([0,-y/2,-z/2])
difference(){
	hull(){
		cylinder(r=x/2, h=z, center=true);
		translate([0,y,0]) cylinder(r=x/2, h=z, center=true);
	}

	hull(){
		cylinder(r=x/2-t, h=z+t, center=true);
		translate([0,y,0]) cylinder(r=x/2-t, h=z, center=true);
	}
}

translate([0,0,t/2])
difference(){
	hull(){
		translate([0,-y/2,0])
		hull(){
			cylinder(r=x/2, h=t, center=true);
			translate([0,y,0]) cylinder(r=x/2, h=t, center=true);
		}
		translate([pipeOffset,0,taperLength])
		cylinder(r=pipeDiameter/2,h=t,center=true);
	}
	hull(){
		translate([0,-y/2,])
		hull(){
			cylinder(r=x/2-t, h=t, center=true);
			translate([0,y,0]) cylinder(r=x/2-t, h=t, center=true);
		}
		translate([pipeOffset,0,taperLength])
		cylinder(r=pipeDiameter/2-t,h=t,center=true);
	}
}

translate([pipeOffset,0,taperLength])
difference(){
cylinder(r=pipeDiameter/2,h=pipeConnectLength);
cylinder(r=pipeDiameter/2-t,h=pipeConnectLength);
}

translate([0,-y/2,-z])
difference(){
	hull(){
		cylinder(r=x/2+lip, h=t, center=true);
		translate([0,y,0]) cylinder(r=x/2+lip, h=t, center=true);
	}

	hull(){
		cylinder(r=x/2-t, h=t, center=true);
		translate([0,y,0]) cylinder(r=x/2-t, h=t, center=true);
	}

    for(j=[0:1]){
        for(i=[-1,1]){
            translate([x*i/2+lip*i/2,y*j,0])
            cylinder(d=screwHoleDia,h=2*t,center=true);

        }
        translate([0,y*j+x*(2*j-1)/2+lip*(2*j-1)/2,0])
        cylinder(d=screwHoleDia,h=2*t,center=true);
    }

}

}

```


[User]
I'm looking to create a customizable name sign with a magnet hole in the back. I'd like to be able to set the text, the overall dimensions of the sign, the text's position, corner rounding, and the size and placement of the magnet hole.

Specifically, I want these parameters to be adjustable:

*   **`text`**: The text to display on the sign.
*   **`sign_length`**: The overall length of the sign.
*   **`text_x`**: The horizontal offset of the text from the left edge of the sign.
*   **`text_y`**: The vertical offset of the text from the bottom edge of the sign.
*   **`sign_width`**: The overall width of the sign.
*   **`sign_thickness`**: The thickness of the sign.
*   **`radius`**: The radius of the rounded corners.
*  **`magnet_radius`**: The radius of the magnet hole.
*   **`magnet_thickness`**: The depth of the magnet hole.
I would also like to use a standard font of the text, I don't need to specify that one.

Could you generate the OpenSCAD code for this?

[Assistant]
parametric-magnetic-name-sign.scad:
```
/*
Parametric Magnetic Name Sign
http://www.thingiverse.com/thing:1278048/

By Paul Houghton
Twitter: @mobile_rat
Email: paulirotta@gmail.com
Blog: http://paul-houghton.com/

Creative Commons Attribution ShareAlike License
https://creativecommons.org/licenses/by-sa/4.0/legalcode
*/

/* [Parametric Magnetic Name Sign options] */

// Sign text
text="Liva";

// Sign length
sign_length=32;

// Text X offset
text_x=3;

// Text Y offset
text_y=3;

// Sign width
sign_width=17;

// Sign thickness
sign_thickness=2; // +2*rounding as with other dimensions

// Radius of corner rounding
radius=3;

// Radius of magnet
magnet_radius=5.2;

// Thickness of magnet
magnet_thickness=5.15;

// Number of facets for approximating a sphere
$fn=10; // #sides to a circle. Try big and small numbers

sign(line1=text);

module sign(line1="Line1") {
    difference() {
        translate([0,0,-radius]) minkowski() {
            cube([sign_length,sign_width,sign_thickness]);
            sphere(r=radius);
        }

        union() {
            translate([text_x,text_y,0]) linear_extrude(height=3) text(line1);
            magnet_hole();
        }
    }
 }

module magnet_hole() {
    translate([-radius+(sign_length + 2*radius)/2,-radius+(sign_width+2*radius)/2,-sign_thickness-radius-.9])
        cylinder($fn=256, r=magnet_radius, h=magnet_thickness);
}
```


[User]
Hey, I'm looking to design a parametric hinge for RC plane control surfaces, and I'd like some help creating the OpenSCAD files. I have some parameters in mind, but I want them to be easily adjustable.

Here's a breakdown of what I need:

*   **Basic Components:** The design should consist of two main parts: a base plate that will be glued into the control surface (like an aileron or elevator) and a hinge cylinder that will rotate around a pin.
*   **Plate Parameters:**
    *   `plate_width`: The width of the base plate (e.g., 15mm).
    *   `plate_length`: The length of the base plate (e.g., 15mm).
    *   `plate_thickness`: The thickness of the base plate (e.g., 1.5mm).
*   **Hinge Parameters:**
    *   `pin_diameter`: The diameter of the hinge pin (e.g., 1mm).
    *   `pin_surround`: The amount of extra material around the pin to create the hinge cylinder wall, (e.g., 2.5mm).
    * `hinge_thickness`: The length of the hinge cylinder (e.g. `plate_width/3`).
    *   `hinge_radius`: Should be calculated as `(pin_diameter + (pin_surround * 2))/2`. This is the outer radius of the cylinder.
*   **Glue Holes:**
    *  `glue_hole_diameter`: The diameter of the holes for glue, (e.g., 3mm)
    *   There should be four glue holes, one in each corner of the plate.
*   **Assembly:**
    *   I need two plate parts.
    *   One plate part needs a single hinge cylinder. The hinge cylinder should be flush with the plate on one side and should only extend from that side.
    *   The second plate part needs two hinge cylinders. Both should be flush with the plate on the same side and should only extend from that side, with one cylinder offset left and the other offset right by `plate_width/3`.
*   **Important Considerations**
    *   The hinge cylinder should include a cutout to allow for movement of the hinge. The cutout should be shaped roughly like a quarter of a cylinder.
    *   The hinge pin hole needs to be centered in the hinge cylinder.

I'd like the file to use OpenSCAD standard libraries (no custom ones, please), and it would be great if it could be easily modified. It should be easy to regenerate the models at different scales, or tweak the clearance around the hinge pin. Could you generate the code for me please?

[Assistant]
RC_Plane_Hinge.scad:
```
plate_width=15;
plate_length=15;
plate_thickness=1.5;
pin_diameter=1;
pin_surround=2.5;
glue_hole_diameter=3;
hinge_radius=(pin_diameter+(pin_surround*2))/2;
hinge_thickness=plate_width/3;

$fa=.5;
$fs=.5;

module base(){
	cube([plate_width,plate_length,plate_thickness]);}
module glue_holes(){
	translate([plate_width/4,plate_length/4,-0.1]){
		#cylinder(r=glue_hole_diameter/2,h=plate_thickness+2);
	}
	translate([plate_width/4*3,plate_length/4,-0.1]){
		#cylinder(r=glue_hole_diameter/2,h=plate_thickness+2);
	}
	translate([plate_width/4*3,plate_length/4*3,-0.1]){
		#cylinder(r=glue_hole_diameter/2,h=plate_thickness+2);
	}
	translate([plate_width/4,plate_length/4*3,-0.1]){
		#cylinder(r=glue_hole_diameter/2,h=plate_thickness+2);
	}
}
module hinge(){
	translate([hinge_thickness,-hinge_radius,hinge_radius]){
		rotate([0,90,0]){
			difference(){
				union(){
					cylinder(r=hinge_radius,h=hinge_thickness);
					difference(){
						rotate([0,0,45]){
							translate([0,-hinge_radius,0]){
							cube([hinge_radius*4.5,hinge_radius*2,hinge_thickness]);
							}
						}
						translate([hinge_radius,-hinge_radius,-hinge_thickness]){
							#cube([30,30,30]);
						}
					}
				}
				translate([-.1,0,0]){
					#cylinder(r=pin_diameter/2,h=hinge_thickness+.2);
				}
			}
		}
	}
}

difference(){
	union(){
		difference(){
			cube([plate_width,plate_length,plate_thickness]);
			rotate([45,0,0]){
				cube(plate_width,30,-30);
			}
		}
		hinge();
	}
	glue_holes();
}

translate([plate_width+2,0,0]){
	difference(){
		union(){
			difference(){
			cube([plate_width,plate_length,plate_thickness]);
			rotate([45,0,0]){
				cube(plate_width,30,-30);
			}
		}
			translate([-plate_width/3,0,0]){
				hinge();
			}
			translate([plate_width/3,0,0]){
				hinge();
			}
		}
		glue_holes();
	}

}
```


[User]
Hi, I'm looking to create a set of interlocking temple pieces for the board game Tikal, similar to what's described in the provided OpenSCAD file. I'd like the design to be parametric so I can easily adjust sizes and tolerances. I'd like to be able to generate the following pieces:

1.  **Temple Layers:** Individual interlocking layers for the temple, from level 1 to 10. Each layer should have a slightly smaller footprint than the one below it, and they should include both the male (posts) and female (recesses) interlock features, so the pieces stack securely.

2.  **Upgrade Pieces:** Individual temple layers, designed for placement on the map and on upgrade cards. These should be like the temple layers, but _without_ the interlock features on the top, so that another layer will not stack on top of it. These should also be from level 2 to 10, as level 1 pieces are used only on map tiles. I'd like to specify the number of copies of each upgrade layer via a parameter, as the board game requires differing numbers of each.

3. **Map Pieces:** These should be inverted versions of the temple layers, to slot into the hexagonal map tiles. These are similar in size to the upgrade pieces, but should be inverted relative to the main temple pieces. There will be one of each layer, 1 to 6.

I'd also like the following functionality:

*   **Parametric Design:** I need to be able to control the overall piece height, width, depth, shrinkage between levels, number of stairs per piece, and the interlock distance.
*   **Staircase Feature:** Each temple layer should have a stepped staircase on one side.
*   **Text Labels:** The top surface of each piece (except for the top piece) should have a text label indicating its level (1-9, 10 on the top piece).
*   **Complete Temple Assembly:** I'd like an option to generate a complete temple stack, as well as a version that has a cutout to see the inner structure.
* **Print Layouts:** It would be great if there were options to automatically lay out all the pieces needed for one game for efficient printing, as well as individual single pieces. These layouts should take into account the dimensions of a Makerbot 2 printer (285mm x 153mm), leaving some room for safe printing.

Could you design an OpenSCAD file (or files) to handle this? Please use the `write/Write.scad` library for text and include its import in the file. Thank you!

[Assistant]
tikal_E.scad:
```
//Select an STL part to generate. "Upgrade_xx" are replacements for the little square components. "Map_x" go on the hexagonal big map tiles. For counts of each component, refer to the instructions.
part = "temple"; // [Upgrade_02,Upgrade_03,Upgrade_04,Upgrade_05,Upgrade_06,Upgrade_07,Upgrade_08,Upgrade_09,Upgrade_10,Map_1,Map_2,Map_3,Map_4,Map_5,Map_6,temple,templecut,upgrades,upgrades_p1,upgrades_p1_L,upgrades_p1_R,upgrades_print2,maps,maps_print3,maps_print3b,oneprint]

//in Millimeters
piece_height=4.5;

//in Millimeters
piece_width=45;

//in Millimeters
piece_depth=40;

//Shrinkage from one piece to the next higher one
shrink_factor=.07;

//Steps of the staircase per piece
stairs_per_piece=3;

small_corner=.92;

large_corner=.85;

//front staircase width
stair_width_percent=.3;

// Pockets in temple piece for an upgrade to lock into. Try values between 0.8 and 1
interlockdistance=.9;

/* [Hidden] */

//Todo: Flip over large temples

/*
Temple pieces for the board game Tikal
Version 7, March 2014
Written by MC Geisler (mcgenki at gmail dot com)

This file defines 10 different temple pieces for the board game Tikal.  Each piece locks into the respective
piece below it for stability. When your temple reaches level 10, it will be a complete Mayan temple.

Beside the functions to print single temple layer pieces and several temple layers in one block, there are
several layouts of pieces for you to choose from:
- temple=1 defines a complete temple in one block
- tempelcut=1 will show a complete temple with a cutout to see the inner structure
- oneprint=1 contains all 10 pieces needed for a single temple. Print it to test the pieces. Note that you
need much more pieces to play Tikal.
- upgrades=1 will show the complete set of upgrade pieces needed for the game Tikal
- maps=1 will show the complete set of map pieces needed. Both the temples on the board and the temples
on the hexagonal land pieces are taken into account. You can use these map pieces so that all temples of
the same level are of the same height. During the game, whenever a hexagon with a temple is put down,
select the corresponding map piece and put it into place.
- upgrades_print1=1, upgrades_print2=1 and maps_print3=1 are fitting each to a makerbot2 printing area.
Together they are all the map and upgrade pieces you need for one Tikal game.

Sorry for the use of German for variable names.
Have fun!

License: Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
You are free to:
    Share - copy and redistribute the material in any medium or format
    Adapt - remix, transform, and build upon the material
Under the following terms:
    Attribution - You must give appropriate credit, provide a link to the license, and indicate if changes were made.
    You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
    NonCommercial - You may not use the material for commercial purposes.
    ShareAlike - If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
*/

use <write/Write.scad>

//fontfilename="orbitron.dxf";
//fontfilename="BlackRose.dxf";
//fontfilename="knewave.dxf";
fontfilename="Letters.dxf";

startstufe=1;
endstufe=10;
anzahlstufen=endstufe-startstufe;// minus one as the last piece is without a step

tempelhoehe=piece_height*1.4;
tempelgroesse=.95;
dach1groesse=tempelgroesse*1.1;
dach2groesse= dach1groesse*.8;
dach3groesse= dach2groesse*.8;
dach4groesse= dach3groesse*.8;
tempeltiefe=.4;

shrinkabsolut=shrink_factor*piece_width;

rand=shrinkabsolut*1.5;

treppenhoehe=piece_height/stairs_per_piece;
treppentiefe=piece_depth/(anzahlstufen*stairs_per_piece)*.56;//the amount to subtract
treppeRaus=piece_depth/2*1.5;

druckabstand=2;

//----------------------------------
// temple pieces
//----------------------------------

function piece_widthjetzt(i) = piece_width-(i-startstufe)*shrinkabsolut;
function piece_widthtotal(i) =  (piece_width-(i/2-startstufe)*shrinkabsolut+druckabstand)*(i-startstufe); //aufsummiert
function piece_depthjetzt(i) = piece_depth-(i-startstufe)*shrinkabsolut;
function piece_depthjetztincltreppe(i) = piece_depthjetzt(i)/2+ treppeRaus-treppentiefe*((i-startstufe)*stairs_per_piece+0) ;
function piece_depthjetztincltreppetotal(i) =   ( piece_depth/2-(i/2-startstufe)*shrinkabsolut/2+ treppeRaus-treppentiefe*(i/2-startstufe)*stairs_per_piece +druckabstand) * (i-startstufe); //aufsummiert

module interlock(i,adder)
{
//	do_interlock(piece_widthjetzt(i),piece_depthjetzt(i),adder, (i==9?1:0) );
	do_interlock(piece_widthjetzt(i),piece_depthjetzt(i),adder, 0);
}

module ebene(i,do_post,do_lock,do_up)
{
	do_ebene(i,piece_widthjetzt(i),piece_depthjetzt(i),do_post,do_lock,do_up);
}

module do_interlock(piece_widthjetzt,piece_depthjetzt,adder, full)
{
	interlockwidth=1.5;
	interlocktiefeproz=.65; //.5
	interlockdist=piece_widthjetzt*.65; //.5
	interlockheight=piece_height/2;

	if (full==0)
	{
		translate([interlockdist/2,0,0])
			cube([interlockwidth+adder,piece_depthjetzt*interlocktiefeproz+adder,interlockheight+adder],center=true);
		translate([-interlockdist/2,0,0])
			cube([interlockwidth+adder,piece_depthjetzt*interlocktiefeproz+adder,interlockheight+adder],center=true);
	}
	else
	{
		cube([interlockdist+interlockwidth+adder,piece_depthjetzt*interlocktiefeproz+adder,interlockheight+adder],center=true);
	}

}

module do_floor(piece_widthjetzt,piece_depthjetzt)
{
	union()
	{
		square ([piece_widthjetzt*small_corner,piece_depthjetzt*small_corner],center = true);
		square ([piece_widthjetzt*large_corner,piece_depthjetzt],center = true);
		square ([piece_widthjetzt,piece_depthjetzt*large_corner],center = true);
		translate([0,-1,0])
			square ([piece_widthjetzt*.6,piece_depthjetzt],center = true);
	}
}

tapering=0*.2;
topcutback=-3.1;

//do_post: post down
//do_lock: carve out on top and writing on top
module do_ebene(i=startstufe,piece_widthjetzt,piece_depthjetzt,do_post,do_lock,do_up)
{
	//main block
	rotate([(do_up==0 && i==10)?-90:0,(do_up==0 && i!=10)?180:0,0])
	difference()
	{
		union()
		{
			if (i<10)
			{

				//main cube, taper the bottom
				mirror([0,0,1])
					translate([0,0,piece_height/2-piece_height*tapering/2])
						linear_extrude(height = piece_height*tapering, center = true, scale=(1-0.1*(tapering)))
						{
							do_floor(piece_widthjetzt,piece_depthjetzt);
						}
				//main cube
				translate([0,0,+piece_height/2-piece_height*(1-tapering)/2])
					linear_extrude(height = piece_height*(1-tapering), center = true, scale=(1-0.05*(1-tapering)))
					{
						do_floor(piece_widthjetzt,piece_depthjetzt);
					}

				//treppe
				translate([0,.025,0])
					for ( j = [0:stairs_per_piece-1])
					{
						assign (treppentiefejetzt = treppeRaus-treppentiefe*((i-startstufe)*stairs_per_piece+j)  )
						{
							translate([0,
									-treppentiefejetzt/2,
									j*treppenhoehe-(piece_height/2-treppenhoehe/2)])
								cube([piece_widthjetzt*stair_width_percent,
									treppentiefejetzt,
									treppenhoehe],center=true);
						}
					}

				//posts out on bottom
				if (i>1 && do_post)
				{
					translate([0,0,-piece_height/2-.1])
					{
						 interlock(i-1,0);
					}
				}
			}
			else
			{
				//top temple

				translate([0,(do_up==0 && i==10)?topcutback:0,0])
				{
					if (do_post)
					{
						translate([0,0,-piece_height/2-.1])
						{
							 interlock(i-1,0);
						}
					}

					assign (tempeltiefejetzt = piece_depthjetzt*tempeltiefe)
					translate([0,0,-piece_height/2+tempelhoehe/2])
					{
						//1. gerader quader
						difference()
						{
							cube ([piece_widthjetzt*tempelgroesse,piece_depthjetzt*tempelgroesse,tempelhoehe],center = true);
							translate([0,-piece_depthjetzt*tempelgroesse/2,tempelhoehe*.1])
								cube([piece_widthjetzt*tempelgroesse/5,piece_depthjetzt*tempelgroesse,tempelhoehe*.8],center=true);
						}

						//dach 1
						translate([0,0,tempelhoehe/2+piece_height/2])
						{
							linear_extrude(height = piece_height, center = true, scale=.95)
								square ([piece_widthjetzt*dach1groesse,piece_depthjetzt*dach1groesse],center = true);

							//dach 2
							translate([0,tempeltiefejetzt*dach2groesse,piece_height/2+piece_height/2])
							{
								linear_extrude(height = piece_height, center = true, scale=.9)
									square ([piece_widthjetzt*dach2groesse,tempeltiefejetzt*dach2groesse],center = true);

								//dach 3
								translate([0,tempeltiefejetzt*(dach2groesse*.9-dach3groesse)/2,piece_height/2+piece_height/2])
								{
									linear_extrude(height = piece_height, center = true, scale=.9)
										square ([piece_widthjetzt*dach3groesse,tempeltiefejetzt*dach3groesse],center = true);

									//dach 4
									translate([0,tempeltiefejetzt*(dach3groesse-dach4groesse)/2,piece_height/2+piece_height/2])
										cube([piece_widthjetzt*dach4groesse,tempeltiefejetzt*dach4groesse*.85,piece_height],center = true);
								}

							}
						}
					}
				}
			}
		}

		if (i<10 && do_lock)
		{
			//locks for the posts
			translate([0,0,piece_height/2+.1])
			{
				 interlock(i,interlockdistance);
			}
		}

		//text
		if (i!=10 && do_lock)
		{
			translate([0,0,piece_height/5])
				writecube(str(i),[0,0,piece_height/4],0,face="top", t=piece_height*.5,h=8,space=6, rotate=0, font=fontfilename);
		}

		if (i==10)
		{
			translate([0,-1.5+((do_up==0 && i==10)?topcutback:0),tempelhoehe+piece_height/5])
				writecube(str(i),[0,0,piece_height/4],0,face="top", t=piece_height*.5,h=6,space=6, rotate=0, font=fontfilename);

			//if (i==10)
			translate([0,10/2+piece_depthjetzt*tempelgroesse*0-topcutback*1.7258+((do_up==0 && i==10)?topcutback:0),piece_widthjetzt/2])
				cube([piece_widthjetzt*2,10,piece_widthjetzt*2],center=true);
		}
	}

}

module templeupsidedown(height)
{
	translate([0,0,(height-startstufe)*piece_height])
		rotate([0,180,0])
			temple(height);
}

module temple(height)
{
	for (i = [startstufe:height])
	{
		translate([0,0,(i-startstufe)*piece_height])
			ebene(i,0,(i==height),1);
	}
}

//----------------------------------
// board game pieces
//----------------------------------

hexmapradius=82/2;

module octogon(type)
{
	if (type=="map")
	{
		cylinder(r=hexmapradius,h=1,$fn=6);
	}
	if (type=="small")
	{
		cylinder(r=11/2,h=10,$fn=6);
	}
	if (type=="large")
	{
		cylinder(r=12/2,h=15,$fn=6);
	}
}

module worker()
{
	//small worker
	%translate([hexmapradius*.75,0,0])
		octogon("small");

	//large workder
	%translate([-hexmapradius*.75,0,0])
		octogon("large");

	//large worker on top
	%translate([0,-3,piece_height*(endstufe+1)+1])
		octogon("large");
}

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

//For Makerbot2 print area
//28.5 L x 15.3 W x 15.5 H cm
printareatotalwidth=285;
printareatotaldepth=153;
printsafe=10;
printareawidth=printareatotalwidth-printsafe;
printareadepth=printareatotaldepth-printsafe;

oneprint_start=1;
oneprint_stop=10;

//----------------------------------
// Model options
//----------------------------------

if (part=="Upgrade_02") ebene(2,1,1,0);
if (part=="Upgrade_03") ebene(3,1,1,0);
if (part=="Upgrade_04") ebene(4,1,1,0);
if (part=="Upgrade_05") ebene(5,1,1,0);
if (part=="Upgrade_06") ebene(6,1,1,0);
if (part=="Upgrade_07") ebene(7,1,1,0);
if (part=="Upgrade_08") ebene(8,1,1,0);
if (part=="Upgrade_09") ebene(9,1,1,0);
if (part=="Upgrade_10") ebene(10,1,1,0);
if (part=="Map_1") templeupsidedown(1);
if (part=="Map_2") templeupsidedown(2);
if (part=="Map_3") templeupsidedown(3);
if (part=="Map_4") templeupsidedown(4);
if (part=="Map_5") templeupsidedown(5);
if (part=="Map_6") templeupsidedown(6);

if (part=="temple")
{
	//ground map
	%octogon("map");

	translate([0,0,1])
	{
		worker();

		//temple
		translate([0,0,piece_height/2])
			temple(endstufe);

	}
}

if (part=="templecut")
{
	//ground map
	%octogon("map");

	translate([0,0,1])
	{
		worker();

		difference()
		{
			//temple
			for (i = [startstufe:endstufe])
			{
				translate([0,0,(i-startstufe)*(piece_height+.1)+piece_height/2])
					ebene(i,1,1,1);
			}
			cube(50);
		}
	}
}

if (part=="upgrades")
{
	//to the left quadrant
	//translate([piece_widthjetzt(2)/2,-piece_depthjetztincltreppe(2)/2,piece_height/2])
	translate([-(piece_widthjetzt(5)+druckabstand)*11,-piece_depthjetztincltreppe(2)/2,piece_height/2])
	{	//rows
		for (i = [2:endstufe])
		{	//columns
			translate([0,piece_depthjetztincltreppetotal(i),0])
			{
				//Number of upgrade layers (for layer 1: on map/map cards)
				for (reihe = [0:lookup(i, [
									//[ 1, 3 ], //only this is on the board / on one card...
									[ 2, 3 ],
									[ 3, 6 ],
									[ 4, 9 ],
									[ 5, 11 ],
									[ 6, 8],
									[ 7, 5 ],
									[ 8, 3 ],
									[ 9, 2 ],
									[ 10, 1 ]
								])
									-1])
				{
					{	//move
						translate([(piece_widthjetzt(i)+druckabstand)*reihe,0,0]) ebene(i,1,1,0);
					}
				}
			}
		}
	}
	//Printarea
	%translate([0,0,-1]) cube([printareawidth,printareadepth,1]);
}

if (part=="upgrades_p1" || part=="upgrades_p1_L" || part=="upgrades_p1_R")
{
	//assign (from = , distance = i*10, r = i*2)
	//to the first quadrant
	translate([piece_widthjetzt(2)/2,-piece_depthjetztincltreppe(2)/2,piece_height/2])
	{	//rows
		for (i = [2:4])
		{	//columns
			translate([0,piece_depthjetztincltreppetotal(i),0])
			{
				//Number of upgrade layers (for layer 1: on map/map cards)
				for (reihe = [(part=="upgrades_p1" || part=="upgrades_p1_L")?0:3
								:(part=="upgrades_p1_L")?2:lookup(i, [
									//[ 1, 3 ], //only this is on the board / on one card...
									[ 2, 3+3 ], //add two of part 4!
									[ 3, 6 ],
									[ 4, 9-2 ], //two missing
								])
									-1])
				{
					{	//move
						translate([(piece_widthjetzt(i)+druckabstand)*reihe,0,0])
						if (i==2)
						{
							if (reihe<3) {ebene(i,1,1,0);}
							else
							if (reihe>4) {templeupsidedown(4);}
							else
							ebene(4,1,1,0);
						}
						else
						{
							ebene(i,1,1,0);
						}
					}
				}
			}
		}
	}
	//Printarea
	%translate([0,0,-1]) cube([printareawidth,printareadepth,1]);
}

if (part=="upgrades_print2")
{
	translate([0,printareatotaldepth*0,0])
	{
		//to the first quadrant
		translate([piece_widthjetzt(2)/2,-piece_depthjetztincltreppetotal(5)+piece_depthjetztincltreppe(2)/2,piece_height/2])
		{	//rows
			for (i = [5:8])
			{	//columns
				translate([0,piece_depthjetztincltreppetotal(i),0])
				{
					//Number of upgrade layers (for layer 1: on map/map cards)
					for (reihe = [0:lookup(i, [
										//[ 1, 3 ], //only this is on the board / on one card...
										[ 5, 11-4 ], //4 zu wenig
										[ 6, 8],
										[ 7, 5+3 ], //3 mehr
										[ 8, 3+4+1 ], //4 mehr
										[ 9, 2 ],
										[ 10, 1 ]
									])
										-1])
					{
						{	//move
							translate([(piece_widthjetzt(i)+druckabstand)*reihe,0,0])
							if (i==7 && reihe>4)
							{
								if (reihe>6)
								{
									translate([0,-10,0])
										ebene(10,1,1,0);
								}
								else
								{
									ebene(9,1,1,0);
								}
							}
							else
							{
								if (i==8 && reihe>2)
								{
									if (reihe>2 && reihe <7)
										translate([(piece_widthjetzt(5)+druckabstand)*(reihe)-(piece_widthjetzt(8)+druckabstand)*(reihe+.9),7,0])
											ebene(5,1,1,0);
									else
										translate([52,2,0])
											templeupsidedown(4);
								}
								else
								{
									ebene(i,1,1,0);
								}

							}
						}
					}
				}
			}
		}
		//Printarea
		%translate([0,0,-1]) cube([printareawidth,printareadepth,1]);
	}
}

if (part=="maps")
{
	translate([-piece_widthjetzt(1)*7,piece_depthjetztincltreppe(1)*2,0])
		for (i = [startstufe:6])
		{
			translate([(piece_widthjetzt(1)+druckabstand)*i,0,piece_height/2])
			{
				//Numbers on map cards
				for (reihe = [0:lookup(i, [
									[ 1, 3 ],
									[ 2, 3 ],
									[ 3, 4 ],
									[ 4, 3 ],
									[ 5, 3 ],
									[ 6, 1]
								])
									-1])
				{
					translate([0,(piece_depthjetztincltreppe(1)+druckabstand)*reihe,0])
						templeupsidedown(i);
				}
			}
		}

	//Printarea
	//%cube([printareawidth,printareadepth,1],center=true);
}

if (part=="maps_print3")
{
	translate([0,2*printareatotaldepth*0,0])
	{
		//to the first quadrant
		translate([piece_widthjetzt(1)/2,piece_depthjetztincltreppe(1)/2,piece_height/2])
		{	//rows
			for (i = [startstufe:6-3])
			{	//columns
				translate([0,(piece_widthjetzt(1)+druckabstand)*(i-1),0])
				{
					//Number of upgrade layers (for layer 1: on map/map cards)
					for (reihe = [0:lookup(i, [
										[ 1, 3+2 ],
										[ 2, 3+2 ],
										[ 3, 4+1 ],
										//[ 4, 3 ],
										//[ 5, 3 ],
										//[ 6, 1]

									])
										-1])
					{
						{	//move
							translate([(piece_depthjetztincltreppe(1)+druckabstand)*reihe,0,0])
								rotate([0,0,90])
									if (i==1)
									{
										if (reihe==3) {templeupsidedown(5);}
										else
										if (reihe>3) {templeupsidedown(6);}
										else
										templeupsidedown(i);
									}
									else
									{
										if (i==2)
										{
											if (reihe>2) {templeupsidedown(5);}
											else
											templeupsidedown(i);
										}
										else
										{
											if (i==3)
											{
												if (reihe>3) {templeupsidedown(4);}
												else
												templeupsidedown(i);
											}
										}
									}
						}
					}
				}
			}
		}
		//Printarea
		%translate([0,0,-1]) cube([printareawidth,printareadepth,1]);
	}
}

druckabstandadder=3;

if (part=="maps_print3b")
{

	translate([0,2*printareatotaldepth*0,0])
	{
		rotate([0,0,90])
		//to the first quadrant
		translate([piece_widthjetzt(1)/2,-3.5*piece_depthjetztincltreppe(1),piece_height/2])
		{	//rows
			for (i = [startstufe:3])
			{	//columns
				translate([0,(piece_widthjetzt(1)+druckabstand+druckabstandadder)*(i-1),0])
				{
					//Number of upgrade layers (for layer 1: on map/map cards)
					for (reihe = [0:1])
					{
						{	//move
							translate([(piece_depthjetztincltreppe(1)+druckabstand+druckabstandadder)*reihe,0,0])
								rotate([0,0,90])
									if (i==1)
									{
										//if (reihe==3) {templeupsidedown(5);}
										//else
										if (reihe==1) {templeupsidedown(6);}
										else
										templeupsidedown(i);
									}
									else
									{
										if (i==2)
										{
											if (reihe==1) {templeupsidedown(5);}
											else
											templeupsidedown(i);
										}
										else
										{
											if (i==3)
											{
												if (reihe==1) {templeupsidedown(4);}
												else
												templeupsidedown(i);
											}
										}
									}
						}
					}
				}
			}
		}
		//Printarea
		%translate([0,0,-1]) cube([printareawidth,printareadepth,1]);
	}
}

if (part=="oneprint")
{
	translate([-piece_widthjetzt(1)*1.5,piece_depthjetztincltreppe(1)/2,0])
	{
		for (i = [oneprint_start:oneprint_stop])
		{
			translate([piece_widthtotal(i)-(i<5?0:piece_widthtotal(5)-piece_widthjetzt(4)/2*0+piece_widthjetzt(5)/2*0),-(piece_depthjetztincltreppe(1)+druckabstand)*(i>4?1:0),piece_height/2])
			{
					ebene(i,1,1,0);

				//large workder
				%translate([0,-3,-17+piece_height/2*0+0*(i==endstufe?tempelhoehe:0)])
					rotate([(i<10?0:-90),0,(i<10?30:0)])
						octogon("large");

			}
		}
	}
	//Printarea
//	%translate([0,0,-.5])
//		cube([printareawidth,printareadepth,1],center=true);
}

```


[User]
Okay, I'm looking to create a fan duct for a 3D printer that will cool both the primary and secondary extruder drivers. I've seen a design that does this, and I want something similar, but I want it to be fully parametric.

The basic idea is a base plate that mounts to the printer frame, with two duct outlets, one for each driver. The duct will have a curved profile directing airflow from a fan (probably 40mm) towards the heatsinks on the drivers. I want the design to be flexible so I can adjust different aspects.

Here's a more detailed breakdown of what I need:

**Overall Structure:**

*   **Base Plate:** A flat piece that attaches to the printer frame. I need to be able to adjust the size and hole placement for this plate so it fits my specific printer setup.
*   **Ducts:** Two curved ducts that extend from the base plate, each directing air toward a driver. I need to be able to adjust the start point, the curve radius, the end point, and the shape and size of the duct's cross-section along its length. I would like the ducting to be rectangular for ease of printing.
*   **Fan Mount:**  A mount to hold a cooling fan (likely 40mm) over the start of the duct. I need to be able to adjust the fan mount hole size and position. I want to ensure the fan mount is designed to keep vibration minimal. The fan mount should be a part of the baseplate.
*   **Guard:** An optional grille or guard over the outlet of the duct to keep fingers out of the fan. This should be toggleable and parameterized (i.e. I can adjust the spacing, bar thickness, and total height).
*   **Print Options:** I would like the ability to output the entire model in one part, or to split the model into two parts (the base plate with the fan mount, and the ducts) so I can print them separately if I want to.

**Specific Parameters I'd Like to Control:**

*   **Base Plate:**
    *   Width and Length
    *   Thickness
    *   Mounting Hole Diameter and positions (at least 2, but possibly more)
    *   Fan Mount Position/Offset on the base plate
*   **Ducts:**
    *   Start point (X, Y, Z) on the base plate
    *   End point (X, Y, Z) over the drivers
    *   Curve Radius
    *   Width, Height, and Taper/Profile of the Duct (at start, middle, and end, with the option to use a linear or spline interpolation between points)
*   **Fan Mount:**
    *   Fan size (40mm for now, but adjustable for other sizes)
    *   Hole Diameter/Spacing
    *   Mounting Depth/Thickness
*   **Guard:**
    *   Enable/Disable
    *   Bar Thickness
    *   Bar Spacing
    *   Guard Height

**Additional Considerations**

*   I'd like the design to be relatively easy to print and avoid any crazy overhangs, I'm using PLA so minimal bridging will work best.
*   I want all of this to be adjustable within the OpenSCAD customizer, and I'd like it to be relatively simple.

I think that covers it. Could you create a parametric OpenSCAD file for me that does all of this? It would be great if you could provide a single file to start. I don't have a lot of experience with CAD, so I don't really know what libraries are available, but if you have any libraries in mind to make things easier let me know!

[Assistant]
bottom_fanduct_v2.scad:
```
/*[Fan Duct]*/
//Print right duct?
right_side = 1;	  //[1:yes,0:no]
//Print left duct?
left_side  = 1;   //[1:yes,0:no]
//Print guard?
guard      = 1;   //[1:yes,0:no]
//How to Print?
print_in_one = 1;   //[1:InOne,0:Seperate]

/*[Hidden]*/
$fa = 5;
$fn = 360 / $fa;

module base()
{
  union()
  {
    difference()
    {
      hull() {
        translate([8,8])
          cylinder(h= 5, d=7);

        cylinder(h= 5, d=7);
      }

      cylinder(h= 5.0, d=3.5);
    }

    hull() {
      translate([8,8])
        cylinder(h= 5, d=7);

      translate([82,8])
        cylinder(h= 5, d=7);
    }

    difference()
    {
      hull() {
        translate([82,8])
          cylinder(h= 5, d=7);

        translate([90,0])
          cylinder(h= 5, d=7);
      }

      translate([90,0])
        cylinder(h= 5, d=3.5);
    }

    difference()
    {
      union () {
        hull() {
          translate([17.5,25])
            cylinder(h=3, d =10);

          translate([72.5,25])
            cylinder(h=3, d =10);
        }

        hull() {
          translate([17.5,25])
            cylinder(h=3, d =10);

          translate([17.5,10])
            cylinder(h=3, d =10);
        }

        hull() {
          translate([72.5,10])
            cylinder(h=3, d =10);

          translate([72.5,25])
            cylinder(h=3, d =10);
        }
      }

      translate([37.5,25-1.75,2])
        cube([35,3.5,1.05]);

      translate([37.5,25])
        cylinder(h=5, d =3.5);

      translate([72.5,25])
        cylinder(h= 5, d=3.5);

      translate([70,22.5,2])
        cube([5,5,1.51]);
    }

    translate([25,27,3])
    {
      // stuetzen
      if (left_side)
      {
        translate([-3.5,-22.5,2])
          cube([7,7,14]);
      }

      if (right_side)
      {
        translate([-3.5+40,-22.5,2])
          cube([7,7,14]);
      }
    }
  }
}

module duct(hollow)
{
  translate([25,27,3])  //translate([5,32,0])
  union() {
    difference()
    {
      union() {

        /*
        // fan
        translate([2,0,2]) minkowski()
        {
          cube([36,2,36]);

          rotate([-90,0,0])
            cylinder(d=4,h=1);
        }
        */

        hull()
        {
          cube([40,3,3]);

          translate([2,0,38])
            rotate([-90,0,0])
              cylinder(d=4, h=3);

          translate([38,0,38])
            rotate([-90,0,0])
              cylinder(d=4, h=3);
        }

        if (left_side)
        {
          // links
          hull(){
            hull() {
              translate([5,-25,20])
                rotate([-90,0,0])
                  cylinder(h=3, d = 15);

              translate([-5,-25,20])
                rotate([-90,0,0])
                  cylinder(h=8, d = 15);
            }

            translate([20,0,20])
              rotate([-90,0,0])
                cylinder(h=3, d = 40);
          }

          hull() {
            translate([5,-25,20])
              rotate([-90,0,0])
                cylinder(h=5, d = 15);

            translate([-5,-25,20])
              rotate([-90,0,0])
                cylinder(h=5, d = 15);
          }
        }

        if (right_side)
        {
          //rechts
          hull(){
            hull() {
              translate([40+5,-25,20])
                rotate([-90,0,0])
                  cylinder(h=8, d = 15);

              translate([40-5,-25,20])
                rotate([-90,0,0])
                  cylinder(h=3, d = 15);
            }

            translate([20,0,20])
              rotate([-90,0,0])
                cylinder(h=3, d = 40);
          }

          hull() {
            translate([40+5,-25,20])
              rotate([-90,0,0])
                cylinder(h=5, d = 15);

            translate([40-5,-25,20])
              rotate([-90,0,0])
                cylinder(h=5, d = 15);
          }
        }
      }

      if (hollow)
      {
        //hohlraum
        union() {
          if (left_side)
          {
            // links
            hull(){
              hull() {
                translate([5,-20,20])
                  rotate([-90,0,0])
                    cylinder(h=3, d = 12);

                translate([-5,-20,20])
                  rotate([-90,0,0])
                    cylinder(h=3, d = 12);
              }

              translate([20,0,20])
                rotate([-90,0,0])
                  cylinder(h=3.1, d = 35);
            }

            hull() {
              translate([5,-25.25,20])
                rotate([-90,0,0])
                  cylinder(h=5.5, d = 12);

              translate([-5,-25.25,20])
                rotate([-90,0,0])
                  cylinder(h=5.5, d = 12);
            }
          }

          if (right_side)
          {
            //rechts
            hull(){
              hull() {
                translate([40+5,-20,20])
                  rotate([-90,0,0])
                    cylinder(h=3, d = 12);

                translate([40-5,-20,20])
                  rotate([-90,0,0])
                    cylinder(h=3, d = 12);
              }

              translate([20,0,20])
                rotate([-90,0,0])
                  cylinder(h=3.1, d = 35);
            }

            hull() {
              translate([40+5,-25.25,20])
                rotate([-90,0,0])
                  cylinder(h=5.5, d = 12);

              translate([40-5,-25.25,20])
                rotate([-90,0,0])
                  cylinder(h=5.5, d = 12);
            }
          }
        }
      }

      if (!right_side && !left_side)
      {
        translate([-9,3,4])
          rotate([90,0,0])
           linear_extrude(height = 3, $fn = 100)
            text("\u263b", 40);
      }

      // loecher
      translate([4,-1,4])
        rotate([-90,0,0])
          cylinder(h=5, d =3.5);

      translate([40-4,-1,4])
        rotate([-90,0,0])
          cylinder(h=5, d =3.5);

      translate([4,-1,40-4])
        rotate([-90,0,0])
          cylinder(h=5, d =3.5);

      translate([40-4,-1,40-4])
        rotate([-90,0,0])
          cylinder(h=5, d =3.5);

      // muttern
      translate([4,-5,4])
        rotate([-90,0,0])
          cylinder(h=5, d =7);

      translate([40-4,-5,4])
        rotate([-90,0,0])
          cylinder(h=5, d =7);

      translate([4,-5,40-4])
        rotate([-90,0,0])
          cylinder(h=5, d =7);

      translate([40-4,-5,40-4])
        rotate([-90,0,0])
          cylinder(h=5, d =7);
    }

    if (guard)
    {
      if (left_side)
      {
        // gitter
        for (a =[1:4])
          translate([a*5-13,-25,14])
            cube([0.5,5,12]);
      }

      if (right_side)
      {
        for (a =[1:4])
          translate([a*5-13+40,-25,14])
            cube([0.5,5,12]);
      }
    }
  }
}

difference()
{
  union ()
  {
    difference()
    {
      base();
        duct(false);
    }

    if (!print_in_one)
    {
      translate([0,40,30])
        rotate([-90,0,0])
          duct(true);
    } else {
      duct(true);
    }
  }

  translate([42.5,4,-0.1])
    cube([25,30,1.6]);

  /*
  translate([12.49,4,-0.1])
    cube([65.02,30,6]);
  */

}
```


[User]
Hey, I'm looking to get a parametric design for a Chinese name stamp. I've got a basic idea, but I need it to be flexible.

Here's what I'm thinking: The stamp should have a base that's a cube, with a slightly smaller cube subtracted from the center to create a kind of raised border. The size of the stamp (that is, the length of the base cube's sides) should be a parameter. Also, the height of the base cube, and the height of the border, should be parameterized, as well as the size of the subtracted cube.

On the top of that base, I want to have the name, rendered in Chinese characters. I need parameters for a last name (which will be a single Chinese character), and first name (which will be two Chinese characters), but I'll also need a general parameter for the font used for all the text. The first and second character in the first name should be placed side-by-side, with a space between them. The last name should be placed centered below the first name. The spacing and size of each character can be relative to the overall size.

Finally, I also want to add a sphere on one side of the stamp (I'd like to be able to parameterize the distance of the sphere from the side of the cube).

The code should be parametric, so I can change all of these settings easily. Please use OpenSCAD. I’d really appreciate it if you could use the BOSL2 library for any of the more complex operations or utility functions.

[Assistant]
Namestamp.scad:
```
// (1 Char)
last_name = "傅";
//Firstname (1st Char)
first_name_1 = "歐";
//Firstname (2nd Char)
first_name_2 = "立";
//Stamp Size (mm)
size = 40; // [15:5:100]
// Google Fonts
font = "PingFang TC:style=Semibold";

//font = "Weibei TC:style=Bold";

height = size*1.5;
$fn = 100 * 1;

rotate([180,0,0])
union(){
difference(){
    cube([size,size,height], center = true);
    translate([0,0,-height/2])
        cube([size-2,size-2,3], center = true);
}
translate([0,0,-height/2 - 0])
    linear_extrude(height=5, scale=1)
    union(){
        translate([-size/20,size/5]) rotate([0,0,90])
            text(first_name_1, font = font, size=size/3, halign="center", spacing = 1);
        translate([size/2.4,size/5]) rotate([0,0,90])
            text(first_name_2, font = font, size=size/3, halign="center", spacing = 1);

        translate([size/3,-size/4])
            rotate([0,0,90])
                scale([1,1.9])
                    text(last_name, font = font, size=size/3, halign="center", spacing = 1);
    }

translate([-size/2.8,0,0]) sphere(r=size/5);
}

```