[User]
Hey, I'm looking for an OpenSCAD design for a customizable fishing rod clip. I have a couple of different rod sections that I want to hold together during travel, and I'd like a clip that I can adjust to fit different rod diameters. Basically, it needs to have two circular "grips" that hold onto the rod sections, connected by a small rectangular piece.

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

*   **`inner_dia_1`**: The inner diameter of the bottom section grip.
*   **`inner_dia_2`**: The inner diameter of the top section grip.
*   **`wedge_angle`**: The angle (in degrees) of the cutout in each grip. This affects how easily the rod snaps in/out and should be adjustable since I use flexible filament sometimes and need the angle to be a bit smaller than when using rigid filament.
*   **`wall_thickness`**: The thickness of the clip's wall.
*   **`distance`**: The distance between the centers of the two circular grips.
*  **`height`**: The height/thickness of the entire clip.
*   **`facets`**: Number of facets to use for circles.

I'd also need the design to automatically calculate the outer diameters of the grips using the inner diameters and wall thickness. I'd like it to work where each "grip" is a circle with a circular hole, and then there is a wedge taken out. Then those two "grips" are connected by a rectangular bar and the whole shape is extruded to the height. It'd be great if you could use the `linear_extrude` function with the center argument set to true. And please center the object on the x-y plane.

The "grips" should be oriented with their circular walls lined up on the y-axis and be offset in the x-direction by `distance`. There should also be a rectangular piece joining them.

Essentially, I'm aiming for a design similar to what's described in the "Updated" sections of these two existing files, but I'd like them created from scratch. It looks like I'll need a `wedge` module that generates a wedge shaped polygon for the cutouts.

I don't need any external libraries for this model. Could you help me with that?

[Assistant]
Rod_Clip_V4.scad:
```
/************** BEST VIEWED IN A CODE EDITOR 80 COLUMNS WIDE *******************
*
* Customizable Fishing Rod Clips
* Benjamen Johnson <workshop.electronsmith.com>
* 20180613
* Version 4
* openSCAD Version: 2015.03-2
*******************************************************************************/

// Diameter of the bottom section
inner_dia_1 = 9;

// Diameter of the top section
inner_dia_2 = 4;

// Wedge angle of the cut (how many degrees of the circle to cut)
wedge_angle = 60;

// Thickness of the clip
wall_thickness = 4;

// Distance between the sections
distance = 15;

// Height of the clip
height = 8;

// how round? The higher the rounder
facets=100;

/*[hidden]*/
// add a slight amount to holes so the model renders properly
render_offset = 0.01;

// calculate the outer diameters
outer_dia_1 = inner_dia_1+2*wall_thickness;
outer_dia_2 = inner_dia_2+2*wall_thickness;

linear_extrude(height+render_offset,center=false, convexity=10, twist=0,center=true)
union() {
// move so the outer wall of the bottom clip lines up with the X-axis
translate([0,outer_dia_1/2,0]) difference() {
    circle(d=outer_dia_1, center=true,$fn=facets);
    circle(d=inner_dia_1, center=true,$fn=facets);
    rotate([0,0,90]) wedge(outer_dia_1*2,height+render_offset,wedge_angle);

}
// move so the outer wall of the top clip lines up with the X-axis
translate([0,outer_dia_2/2,0]) difference() {
    translate([distance,0,0])circle(d=outer_dia_2, center=true,$fn=facets);
    translate([distance,0,0])circle(d=inner_dia_2, center=true,$fn=facets);
    translate([distance,0,0])rotate([0,0,90]) wedge(outer_dia_2*2,height+render_offset,wedge_angle);

}
// join the two clips
translate([distance/2,wall_thickness/2])square([distance,wall_thickness],center=true);
}

module wedge(x=1,z=1,angle=45){
    angle=angle/2;

        polygon([[0,0],
              [x*cos(angle),x*sin(angle)],
              [x*cos(angle),-x*sin(angle)]]);

    }
```
Rod_Clip_V5.scad:
```
/************** BEST VIEWED IN A CODE EDITOR 80 COLUMNS WIDE *******************
*
* Customizable Fishing Rod Clips
* Benjamen Johnson <workshop.electronsmith.com>
* 20180613
* Version 5
* openSCAD Version: 2015.03-2
*******************************************************************************/

// Diameter of the bottom section
inner_dia_1 =9;

// Diameter of the top section
inner_dia_2 = 4;

// Wedge angle of the cut (how many degrees of the circle to cut)
wedge_angle = 60;

// Thickness of the clip
wall_thickness = 4;

// Distance between the sections
distance = 15;

// Height of the clip
height = 8;

// how round? The higher the rounder
facets=100;

// calculate the outer diameters
outer_dia_1 = inner_dia_1+2*wall_thickness;
outer_dia_2 = inner_dia_2+2*wall_thickness;

linear_extrude(height, convexity=10, twist=0,center=true)
union() {
// move so the outer wall of the bottom clip lines up with the X-axis
translate([0,outer_dia_1/2,0]) difference() {
    circle(d=outer_dia_1, center=true,$fn=facets);
    circle(d=inner_dia_1, center=true,$fn=facets);
    rotate([0,0,90]) wedge(outer_dia_1*2,wedge_angle);

}
// move so the outer wall of the top clip lines up with the X-axis
translate([0,outer_dia_2/2,0]) difference() {
    translate([distance,0,0])circle(d=outer_dia_2, center=true,$fn=facets);
    translate([distance,0,0])circle(d=inner_dia_2, center=true,$fn=facets);
    translate([distance,0,0])rotate([0,0,90]) wedge(outer_dia_2*2,wedge_angle);

}
// join the two clips
translate([distance/2,wall_thickness/2])square([distance,wall_thickness],center=true);
}

module wedge(x=1,angle=45){
    angle=angle/2;

        polygon([[0,0],
              [x*cos(angle),x*sin(angle)],
              [x*cos(angle),-x*sin(angle)]]);

    }
```


[User]
Hi, I'm looking to create a set of parametric pipe connectors for DIY projects, specifically for use with PVC or conduit pipes. I've seen some good examples online, but I'd like to be able to customize the dimensions easily.

I'd like you to generate OpenSCAD files for a few different types of connectors, similar to the ones in the description. I need an elbow, a straight connector, a 3-way corner, a 3-way tee, a 4-way edge connector, a 4-way flat connector, a 5-way connector, and a 6-way connector.

Each connector should have the following parameters:

*   `inner_diameter`: This will be the inner diameter of the pipe the connector is intended for.
*   `wall_thickness`: This will be the wall thickness of the connector itself.
*  `length`: This is the length from the center of the fitting to the end of a port.
*   `recess_thickness`: This parameter controls how much smaller the inner diameter of the recessed part of the port is, relative to the `inner_diameter`.
*   `recess_depth`: This is how far the pipe enters into the fitting’s recess.

The connectors should be designed with recesses to allow pipes to slot inside them for either a friction fit or to be glued. Also, make sure to include fillets on the outer edges where two ports meet.

I think it will be important to use modules and functions to make things cleaner - and also to make it easier to add new fitting types in the future if I need to.
I need all of the connectors in one library of `.scad` files - I don't need them to be split up into their own files.

Could you generate all the necessary OpenSCAD files for this?

[Assistant]
Elbow.scad:
```
////////////////////////////////////////////////////////
// Created by Paul Tibble - 11/5/18                   //
// https://www.thingiverse.com/Paul_Tibble/about      //
// Please consider tipping, if you find this useful.  //
////////////////////////////////////////////////////////

//use the outer diameter of pipe
inner_diameter = 20;
//
wall_thickness = 3;
//from center of fitting to end of port
length = 30;
//use the wall thickness of the pipe
recess_thickness = 2;
//how far the pipe enters into the fitting
recess_depth = 15;

//do not change these
$fn = 100*1;
inner_radius = inner_diameter/2;
outer_diameter = inner_diameter + (wall_thickness*2);
outer_radius = outer_diameter/2;
fillet_radius = wall_thickness/2;

module create_center_bore() {
    sphere(inner_radius+0.001);
}

module create_center(){
    sphere(outer_radius+0.001);
}
module create_center_recess_bore() {
    sphere((inner_radius-recess_thickness)+0.001);
}

module create_center_recess(){
    sphere(inner_radius+0.001);
}

module create_x_recess(){
    rotate([0,90,0])translate([0,0,(length-recess_depth)/2])cylinder(length-recess_depth,inner_radius+0.001,inner_radius+0.001,true);
}
module create_x_recess_bore(){
    rotate([0,90,0])translate([0,0,length/2])cylinder(length,(inner_radius-recess_thickness),(inner_radius-recess_thickness),true);
}
module create_x_bore(){
    rotate([0,90,0])translate([0,0,length])cylinder(length*2,inner_radius,inner_radius,true);
}
module create_x_outer(){
    rotate([0,90,0]){
        rotate_extrude(angle=360,convexity=10){
            square([outer_radius,length-fillet_radius]);
            translate([outer_radius-fillet_radius,length-fillet_radius,0])circle(fillet_radius);
            translate([0,length-fillet_radius,0])square([outer_radius-fillet_radius,fillet_radius]);
        }
    }
}

module create_mx_recess(){
    rotate([0,0,180])create_x_recess();
}
module create_mx_recess_bore(){
    rotate([0,0,180])create_x_recess_bore();
}
module create_mx_bore(){
    rotate([0,0,180])create_x_bore();
}
module create_mx_outer(){
    rotate([0,0,180])create_x_outer();
}

module create_y_recess(){
    rotate([0,0,90])create_x_recess();
}
module create_y_recess_bore(){
    rotate([0,0,90])create_x_recess_bore();
}
module create_y_bore(){
    rotate([0,0,90])create_x_bore();
}
module create_y_outer(){
    rotate([0,0,90])create_x_outer();
}

module create_my_recess(){
    rotate([0,0,-90])create_x_recess();
}
module create_my_recess_bore(){
    rotate([0,0,-90])create_x_recess_bore();
}
module create_my_bore(){
    rotate([0,0,-90])create_x_bore();
}
module create_my_outer(){
    rotate([0,0,-90])create_x_outer();
}

module create_z_recess(){
    rotate([0,-90,0])create_x_recess();
}
module create_z_recess_bore(){
    rotate([0,-90,0])create_x_recess_bore();
}
module create_z_bore(){
    rotate([0,-90,0])create_x_bore();
}
module create_z_outer(){
    rotate([0,-90,0])create_x_outer();
}

module create_mz_recess(){
    rotate([0,90,0])create_x_recess();
}
module create_mz_recess_bore(){
    rotate([0,90,0])create_x_recess_bore();
}
module create_mz_bore(){
    rotate([0,90,0])create_x_bore();
}
module create_mz_outer(){
    rotate([0,90,0])create_x_outer();
}
//difference(){
union(){
    difference(){
        union(){
            create_center();
            create_x_outer();
            //create_mx_outer();
            create_y_outer();
            //create_my_outer();
            //create_z_outer();
            //create_mz_outer();
        }
        create_center_bore();
        create_x_bore();
        //create_mx_bore();
        create_y_bore();
        //create_my_bore();
        //create_z_bore();
        //create_mz_bore();
    }

    difference(){
        union(){
            create_center_recess();
            create_x_recess();
            //create_mx_recess();
            create_y_recess();
            //create_my_recess();
            //create_z_recess();
            //create_mz_recess();
        }
        create_center_recess_bore();
        create_x_recess_bore();
        //create_mx_recess_bore();
        create_y_recess_bore();
        //create_my_recess_bore();
        //create_z_recess_bore();
        //create_mz_recess_bore();
    }
}
//translate([0,0,-100])cube([200,200,200],true);
//}
```


[User]
Okay, I'd like to create a parametric funnel design, specifically for use with a Hydro Flask, but I want to make it adaptable for different sized bottles, not just the one I have.

Right now, I have a design that's based on the 40oz Hydro Flask, which has a mouth diameter of 53.64mm. It also uses some hard-coded values for things like the wall thickness, height of the funnel, and the bottom diameter of the funnel. I want to make those into parameters that I can easily adjust.

Here's the breakdown of what I'm looking for:

1.  **Parametric Hydro Flask Opening Diameter (`hydro_diameter`):** The user should be able to specify the diameter of the bottle's mouth (the smallest diameter the funnel needs to fit into).
2.  **Clearance Parameter (`clearance`):** The user should be able to specify the amount of clearance between the bottle's mouth and the funnel's inner edge. For my current design, that's 2mm.
3. **Wall Thickness (`wall_thickness`):** The thickness of the funnel wall. My current design is 3mm.
4.  **Funnel Height (`funnel_height`):**  The height of the funnel section. The current design uses 80mm.
5.  **Bottom Diameter (`bottom_diameter`):** The diameter of the larger, open end of the funnel.  I'd like this to be adjustable, it's 120mm in my existing design.
6.  **Bottom Ring:** At the bottom of the funnel, I'd like a small ring feature that helps the funnel rest on the edge of a container. The ring should be parametric for both its outer and inner diameters, and for it's height.
7. **Half-Moon Lip:** Include a half-moon shaped lip, which should also be parametric so I can adjust how far it sticks out, along with how wide and thick it is.
8.  **Fillet**: I'd also like to add a fillet between the straight wall and the bottom ring, it should have a radius parameter.

I also want to specify the fineness of the curves, the $fn variable. Let's add a parameter called `$fn` for that.

The design involves a lofted shape to make the conical funnel. My design uses `rotate_extrude`, `intersection`, and `difference` commands, plus a `circle`, `square`, and `cylinder`. I don't think there is a need for any libraries for this design.

I think that about covers it. I'd like to get the resulting code back in a single .scad file if possible.

[Assistant]
HydroFlaskFunnel-v1.scad:
```
/*
 *  Name: HydroFlaskFunnel.scad
 *  Description: Funnel for HydroFlask
 *  Author: Anhtien Nguyen
 *  Date: June 13, 2018
 */

//
//*********************************************************************
// hydro-flask diameter (smallest)
hd = 53.64; // 40oz hydro-flask
// clearance
clearance= 2;
// thickness
wall = 3;
// funnel height
ht = 80;
// bottom diameter (largest)
bd = 120;
or = hd/2-clearance; // outer radius top
x0 = bd/2-or; // radius  bottom

ir = or - wall; // inner radius, top

function sqr(x) = x * x;
xc = (sqr(ht) + sqr(x0)) / (2 * x0);

makeFunnel();

module makeFunnel()
{
	$fn = 240;

	rotate_extrude()
	translate([ir, 0])
	intersection() {
		difference() {
			square([x0 + wall, ht]);
			translate([xc + wall, ht]) circle(xc);
		}
		translate([xc + wall, ht]) circle(xc + wall);
	}
	//  ring at bottom
	difference() {
		cylinder(h = 2, r = x0+or+((x0+or)/15));
		translate([0, 0, -1])
		cylinder(h = 4, r = x0+or-3);
	}

	// Half moon lip
	translate([-x0-or,0,0]){
		difference() {
			cylinder(r=20, h=2);
			translate([25,0,0]) cube(50, center=true);
		}
	}
}

```


[User]
Hi there, I'm looking to design some plant labels, and I'd like to use OpenSCAD. I have some pretty specific ideas, and it's a bit more involved than just a cube with text, so I'm hoping you can help.

Essentially, I need a parametric design for a plant label that includes a flat label part and one or two stick parts that go into the ground. I want to be able to control things like the overall label size, the text, the font, the stick dimensions, and how the text is rendered on the label. I'd like to support text both on top of the label (protruding) or as an indent in the label surface.

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

*   **`lettering`**: The primary text string for the label.
*   **`lettering2`**: An optional secondary text string that appears below the main text on the label or not at all.
*   **`style`**: The font to use for the text. I need it to include a large selection of Google fonts, like the ones listed in the original scad file - "Lobster", "Abril Fatface", "Aclonica", etc, and it should work as a string variable.
*   **`stickLength`**: The length of the stick part(s) that go into the soil.
*   **`labelWidth`**: The overall width of the label part.
*   **`labelHeight`**: The overall height of the label part. If there are two lines of text, the label height should double to accomodate.
*   **`textSpacing`**: The spacing between the letters in the text.
*   **`textSize`**: The size of the text.
*   **`textNudgeX`**: A small adjustment in the x direction for text placement.
*   **`textNudgeY`**: A small adjustment in the y direction for text placement.
*   **`textHeight`**: The height of the text if it's raised or the depth of the text if it is indented.
*   **`labelStyle`**: A string to select the text rendering style, "Cutout", "OnTop", or "Indent". For cutout, the lettering should be a cut out in the label part. For "OnTop", the lettering should be raised from the surface. For "Indent", the text should be indented into the label surface.
*   **`labelDepth`**: The thickness of the label part.
*  **`indentDepth`**: How deep to indent the text, should only apply if `labelStyle` is "Indent".
*  **`indentEdgeWidth`**: How wide to indent, should only apply if `labelStyle` is "Indent".
*  **`stickWidth`**: The width of the stick part(s).
*  **`oneOrTwoSticks`**: A string to select one or two stick parts, either "One" or "Two"
*   **`print`**: A string value that should be "all" to produce the entire model, or "text" to print only the stick.

The label part should be a rectangular block, with filleted edges for a rounded look. The stick parts should be rectangular, with a pointed end. The main text (`lettering`) should always be present; the second text (`lettering2`) is optional, and should be placed below the main text if present. If both lines of text are present the label height should be doubled to accomodate. The rendering of the text (cutout, on top, indent) is determined by `labelStyle`.

Could you generate the necessary OpenSCAD code to handle all of this? I'm not using any specific libraries in the project at the moment.

[Assistant]
PlantLabel.scad:
```
lettering = "Briza";
lettering2 = "Media";

// font
style = "Lobster"; //[Abril Fatface,Aclonica,Acme,Advent Pro,Akronim,Aladin,Alegreya,Alegreya Sans,Alegreya SC,Alex Brush,Alfa Slab One,Alice,Amaranth,Architects Daughter,Archivo Black,Archivo Narrow,Arimo,Arvo,Asap,Astloch,Asul,Averia Libre,Averia Serif Libre,Bangers,Basic,Belgrano,Berkshire Swash,Bigshot One,Bilbo Swash Caps,Black Ops One,Bonbon,Bowlby One SC,Brawler,Bubblegum Sans,Butterfly Kids,Cabin Condensed,Caesar Dressing,Cagliostro,Calligraffitti,Capriola,Carter One,Changa One,Chango,Chelsea Market,Cherry Cream Soda,Chewy,Chicle,Chivo,Clicker Script,Coming Soon,Concert One,Condiment,Cookie,Courgette,Covered By Your Grace,Crimson Text,Dancing Script,DejaVu Sans,DejaVu Serif,Dhurjati,Doppio One,Dosis,Droid Sans,Eagle Lake,Electrolize,Emilys Candy,Encode Sans,Encode Sans Compressed,Euphoria Script,Exo,Exo 2,Faster One,Federo,Finger Paint,Fjord One,Fontdiner Swanky,Freckle Face,Fruktur,Gabriela,Geo,Germania One,Give You Glory,Gloria Hallelujah,Goudy Bookletter 1911,Graduate,Grand Hotel,Great Vibes,Griffy,Hanalei Fill,Happy Monkey,Henny Penny,Hind,IM Fell English SC,Indie Flower,Irish Grover,Italianno,Jacques Francois Shadow,Jolly Lodger,Josefin Slab,Joti One,Judson,Just Another Hand,Kalam,Kameron,Karma,Kavoon,Knewave,Kranky,Kristi,Laila,Lakki Reddy,Lancelot,Lato,Leckerli One,Ledger,Lekton,Lemon One,Liberation Sans,Libre Caslon Text,Life Savers,Lilita One,Lily Script One,Limelight,Lobster,Lobster Two,Londrina Outline,Londrina Shadow,Londrina Solid,Lora,Love Ya Like A Sister,Loved by the King,Lovers Quarrel,Luckiest Guy,Lusitana,Macondo,Macondo Swash Caps,Mako,Marck Script,Margarine,Marko One,Maven Pro,McLaren,MedievalSharp,Merienda One,Merriweather,Mervale Script,Metal Mania,Metrophobic,Milonga,Miltonian Tattoo,Miss Fajardose,Molle,Montez,Montserrat,Mr De Haviland,Mystery Quest,Neucha,New Rocker,Niconne,Nosifer,Nothing You Could Do,Noto Sans Oriya,Noto Serif,Nova Square,Nunito,Old Standard TT,Oleo Script,Oleo Script Swash Caps,Orbitron,Oregano,Orienta,Original Surfer,Oswald,Over the Rainbow,Overlock,Oxygen,Pacifico,Paprika,Parisienne,Patrick Hand SC,Paytone One,Peralta,Permanent Marker,Piedra,Pirata One,Play,Playball,Poetsen One,Poppins,Press Start 2P,Princess Sofia,PT Mono,Qwigley,Racing Sans One,Raleway,Rancho,Ranga,Ribeye,Roboto,Roboto Condensed,Roboto Slab,Rochester,Rock Salt,Rubik One,Sail,Salsa,Sansita One,Sarina,Satisfy,Schoolbell,Seaweed Script,Sevillana,Shadows Into Light,Shadows Into Light Two,Share,Six Caps,Skranji,Source Sans Pro,Spicy Rice,Stardos Stencil,Stoke,Syncopate,Teko,Terminal Dosis,The Girl Next Door,Tillana,Timmana,Titillium Web,Ubuntu,Ultra,Underdog,UnifrakturCook,UnifrakturMaguntia,Vampiro One,Vidaloka,Viga,Voces,Volkhov,Vollkorn,Voltaire,Waiting for the Sunrise,Wallpoet,Wellfleet,Wendy One,Yellowtail,Yesteryear,Zeyada]
stickLength = 70; //[1:300]
labelWidth = 88; //[1:300]
labelHeight = 22; //[1:100]
textSpacing = 1; //[1:10]
textSize = 12; //[1:50]
textNudgeX = -4; //[-300:300]
textNudgeY = -2; //[-50:50]

translateY = 0;
textHeight = 1.5; //[1:10]

module label1() {
    difference() {
        cube ([labelWidth, labelHeight, labelDepth], center=true);
        translate([textNudgeX/10,textNudgeY/10,-(labelDepth+1)/2])
        linear_extrude(height=textHeight)
        text (lettering,  size=textSize, font = style, halign = "center", valign = "center", spacing = textSpacing);
    };
}

module label2() {
        if (print != "text") {
            if (lettering2 == "")
                difference(){
                    cube ([labelWidth, labelHeight, labelDepth], center=true);
                    filletBlock(labelWidth,labelHeight,labelDepth);
                }
               else
                difference(){
                    cube ([labelWidth, labelHeight*2, labelDepth], center=true);
                    filletBlock(labelWidth,labelHeight*2,labelDepth);
                }
        }
        echo (lettering2);
        if (lettering2 != "") {
            translate([textNudgeX/10,textNudgeY/10+8,(labelDepth)/2])
            linear_extrude(height=textHeight)
            text (lettering,  size=textSize, font = style, halign = "center", valign = "center", spacing = textSpacing);
            translate([textNudgeX/10,textNudgeY/10-7,(labelDepth)/2])
            linear_extrude(height=textHeight)
            text (lettering2,  size=textSize, font = style, halign = "center", valign = "center", spacing = textSpacing);
         } else {
            translate([textNudgeX/10,textNudgeY/10,(labelDepth)/2])
            linear_extrude(height=textHeight)
            text (lettering,  size=textSize, font = style, halign = "center", valign = "center", spacing = textSpacing);
         }
}

module label3() {
    %difference() {
        translate([0,0,-0.5])
        cube ([labelWidth, labelHeight, labelDepth-1], center=true);
//        %translate([0,0,2*labelDepth/2+labelDepth/2-indentDepth])
//        %cube ([labelWidth-indentEdgeWidth, labelHeight-indentEdgeWidth, 2*labelDepth], center=true);
    }
    difference() {
        translate([0,0,0.5])
        cube ([labelWidth, labelHeight, labelDepth-1], center=true);
        translate([0,0,2*labelDepth/2+labelDepth/2-indentDepth])
        cube ([labelWidth-indentEdgeWidth, labelHeight-indentEdgeWidth, 2*labelDepth], center=true);
    }
    translate([textNudgeX/10,textNudgeY/10,(labelDepth)/2-indentDepth])
        linear_extrude(height=textHeight)
        text (lettering,  size=textSize, font = style, halign = "center", valign = "center", spacing = textSpacing);
}

module stick() {
    echo (lettering2);
    translateY=-stickLength/2-(lettering2==""?labelHeight/2:labelHeight*2/2);
    translate([0,translateY,0])
    difference() {
        cube([stickWidth,stickLength,labelDepth],center=true);
        translate([stickWidth/2,-stickLength/2,0])
        rotate([0,0,45])
        cube([stickWidth,stickWidth,labelDepth+1],center=true);
        translate([-stickWidth/2,-stickLength/2,0])
        rotate([0,0,45])
        cube([stickWidth,stickWidth,labelDepth+1],center=true);
    }
}

module filletBlock(w,h,d) {
    translate([-(w/2-2.5),-(h/2-2.5),0])
    fillet(5,d+1);
    translate([-(w/2-2.5),(h/2-2.5),0])
    rotate([0,0,-90])
    fillet(5,d+1);
    translate([(w/2-2.5),(h/2-2.5),0])
    rotate([0,0,180])
    fillet(5,d+1);
    translate([(w/2-2.5),-(h/2-2.5),0])
    rotate([0,0,90])
    fillet(5,d+1);
}
module fillet(r, h) {
    $fn = 60;
    difference() {
        cube([r + 0.01, r + 0.01, h], center = true);

        translate([r/2, r/2, 0])
            cylinder(r = r, h = h + 1, center = true);

    }
}
labelStyle = "OnTop"; //[Cutout,OnTop,Indent]
labelDepth = 5; //[1:10]
//only used for indent style; no sense to make this more than Label Depth; value =
indentDepth = 1.5;
// good idea to make this a multiple of nozzle size
indentEdgeWidth = 2.40;
stickWidth = 10; //[1:50]
oneOrTwoSticks = "One"; //[One, Two]

//print = "stick";
print = "all";

if (labelStyle == "Cutout") {
    label1();
} else if (labelStyle == "OnTop") {
    label2();
} else {
    label3();
}

if (oneOrTwoSticks == "One") {
    if (print=="text")
        %stick();
    else
        stick();
} else {
    translate([labelWidth/2-stickWidth/2,0,0])
    stick();
    translate([-(labelWidth/2-stickWidth/2),0,0])
    stick();
}
```


[User]
I'd like to create a customizable Pacman ghost that can hold a cylindrical magnet. I've seen some existing designs, but I need one that's fully parametric and lets me easily adjust things like the ghost's size, the magnet's dimensions, and whether it's an enclosed cavity or a bottom access hole for the magnet.

Specifically, I envision a design where I can control:

*   **Overall Ghost Size:** The diameter of the main body (which is like a cylinder with a sphere on top) should be adjustable, and the ghost's overall height should scale appropriately.
*   **Keychain Option:** An option to add a keychain loop at the top is necessary. I need to be able to enable or disable this with a simple parameter.
*   **LED Hole Option:** I'd like the ability to add a hole through the bottom for an LED, with a choice of common LED sizes (like 5mm, 8mm, or 10mm). If I choose not to have one, no hole is generated.
*   **Magnet Cavity:** The main feature here! It needs to support these variations:
    *   **No Magnet:** I should be able to simply turn off the magnet cavity if I don't want one.
    *   **Enclosed Cavity:**  A cylindrical cavity inside the ghost, right below the top, which is completely enclosed.
    *   **Bottom Access:**  A hole through the bottom of the ghost that leads to the magnet cavity. This would allow insertion after printing.
*   **Magnet Dimensions:** The diameter and height of the magnet should be configurable, as well as the depth of the magnet cavity relative to the top surface of the ghost.
*   **Detail Level:**  A parameter to adjust the resolution of the curves, similar to the `$fn` variable.
*   **Preview Option:**  A way to see a "cut-away" view in OpenSCAD to check how the internal features are working - like a temporary cut-away box to help with design.

I'm picturing this being created from a cylinder, sphere, and boolean operations. I don't really care how the 'scallops' at the bottom are generated (maybe a loop and cylinders?), or how the keychain loop is generated. I'm pretty sure this isn't too terribly difficult to implement using the standard `OpenSCAD` libraries, though if you want to make use of something like `BOSL`, `BOSL2`, etc... that's fine too.

Can you provide the code for the necessary `.scad` file?

[Assistant]
pacman-ghost-magnet.scad:
```
//
// Pacman Ghost
// ------------
//
// Created by Joao Alves (jpralves@gmail.com)
// ------------------------------------------
//
// Parameters:

// Figure diameter
diameter = 40; // [25:5:100]

height = diameter*0.65;
// Add keychain
key_chain = 0; // [1:On, 0:Off]
// Add LED hole
led_Hole = 0; // [0:Off, 5:5mm, 8:8mm, 10:10mm]
// Add magnet cavity
magnet_Hole = 1; // [2:Open Bottom, 1:Enclosed, 0:Off]
// Diameter of cylindrical magnet
magnet_Diameter = 12.7;
// Height of cylindrical magnet
magnet_Height = 6.35;
// Relative depth of the magnet
magnet_Location = 5; // [0:1:20]

// Detail
$fn=100; // [20:Low Res, 50:Normal Res, 100:Hi Res]

// Cross Section to see if it looks right internally
look_Inside = 1; // [1:On, 0:Off]

rotate([0,0,-90])
difference() {
  union() {
    cylinder(d = diameter, h = height);
    translate([0, 0, height]) sphere(d = diameter);
    if (key_chain == 1) {
      translate([0, 0, height+diameter/2])
        rotate([0, 90, 0])
          rotate_extrude()
            translate([4, 0, 0])
              circle(r = 1.5);
    }
  }
  translate([diameter/2, -diameter/4, height])
    sphere(d = diameter/3.5);
  translate([diameter/2, diameter/4, height])
    sphere(d = diameter/3.5);
  translate([0, 0, -0.1])
    for(a = [0:11]) {
      rotate([a/12*360,-90,0])
        scale([1.5,1,1])
          translate([0,0,diameter/12.5])
            cylinder(diameter, 0,diameter/25*6.5, $fn=4);
    }

  if (led_Hole > 0) {
    union() {
      translate([0,0,-.01]) cylinder(d = led_Hole+2, h = height-(led_Hole+2)+.01);
      translate([0, 0, height-(led_Hole+2)]) sphere(d = led_Hole+2);
    }
  }

  if (magnet_Hole == 1) translate([0, 0, height + diameter/2-(magnet_Height+magnet_Location)]) cylinder(d = magnet_Diameter+.5, h = magnet_Height+.5, $fn=20);
  else if (magnet_Hole == 2) translate([0, 0, -.01]) cylinder(d = magnet_Diameter+.5, h = (height + diameter/2-(magnet_Location)+.5), $fn=20);

  if (look_Inside > 0) translate([0,0,-.01]) cube([diameter,diameter,diameter*2]);
}
```


[User]
Hey, I'm looking to design a model train signal for my 1.5"-to-the-foot scale railway, and I need a little help with OpenSCAD. I've got some specific needs, and I'd like the design to be parametric so I can tweak it easily.

Here's the deal:

*   **Scale & Application:** This signal is for a 1.5" to the foot scale model railroad, and it's meant to attach to a Carlon 3/4" Type E conduit outlet box, which in turn mounts on 3/4" PVC pipe. So, I need to accommodate that mounting configuration.
*   **LEDs:** The signal lights will be 18mm diameter LEDs - the "eagle eye" type you see on eBay. I expect to use red, yellow, and green LEDs, and these require 12V.
*   **Signal Aspects:** I want to be able to easily switch between a two-aspect (red/green), and a three-aspect (red/yellow/green) signal design. I will need to have the LED openings spaced correctly.
*   **Mounting:** I need a backplate that will support the whole assembly. This backplate should have mounting holes for attaching to the Carlon box. I'd like to specify the screw locations and sizes, and possibly offset the whole signal.
*   **Shrouds:** Each light will need a shroud to block out extraneous light and improve visibility. These shrouds should have a sort of visor over them, and be able to slide into the backplate.
*   **Parametric Design:** Everything – the backplate size, signal light diameter, shroud dimensions, spacing between lights, screw hole spacing, backplate thickness, shroud thickness, height, etc. – should be defined as parameters so I can easily adjust them. I'm most likely going to use metric units, but if you could support both metric and imperial, that would be even better.

I'd really appreciate it if you could generate the necessary OpenSCAD files to accomplish this. I'm not particularly experienced with creating complex OpenSCAD designs from scratch, so having a fully parametric file would be a big help! I don't have any existing code to build on.

Specifically, could you make sure I can change:

*   The number of signal aspects (2 or 3)
*   The backplate dimensions, including thickness and width
*   The signal light diameter
*   The diameter of the shroud
*   The thickness of the shroud
*   The height of the shroud
*   The distance between signal lights
*   The screw spacing and diameter
*   The vertical and horizontal offsets of the backplate

It sounds complex, I know, but I'm hoping for a good starting point I can customize. I need to be able to 3D print this, so it has to be a solid design.

[Assistant]
flls-signal.scad:
```
// signal for Finger Lakes Live Steamers 1.5" to the foot model railway

// customizer
// http://customizer.makerbot.com/docs

// How many aspects?
aspect_count = 0.5; // [0.5: two aspects, 0:three aspects]

thickness_of_backplate = 3 * 1;
width_of_backplate = 70 * 1;
diameter_of_signal = 3 / 8 * 25.4 + .7;
diameter_of_ring = 3 / 4 * 25.4;
shroud_thickness = 1.5 * 1;
buffer = diameter_of_ring / 2 - shroud_thickness;
shroud_height = 1.25 * 25.4;
spacing = (1 + 5 / 8) * 25.4;
screw_spacing = (5 + 5/16) * 25.4;
screw_radius = 3 * 1;

spacing1 = spacing * (0 + aspect_count);
spacing2 = spacing * (1 + aspect_count);
spacing3 = spacing * (2 + aspect_count);

if (spacing * 2 + width_of_backplate *2 < screw_spacing) { echo("no good"); }

difference() {
    union() {
        cylinder(r = width_of_backplate / 2, h = thickness_of_backplate);
        translate([0, -width_of_backplate/2, 0])
            cube([spacing *2, width_of_backplate, thickness_of_backplate]);
        translate([spacing*2, 0, 0])
            cylinder(r = width_of_backplate / 2, h = thickness_of_backplate);
    }
    translate([0,0,-0.01]) {
        translate([spacing1, 0, 0])
            cylinder(r = diameter_of_signal/2, h = thickness_of_backplate + .02);
        translate([spacing2, 0, 0])
            cylinder(r = diameter_of_signal/2, h = thickness_of_backplate + .02);
        if (! aspect_count) {
            translate([spacing3, 0, 0])
                cylinder(r = diameter_of_signal/2, h = thickness_of_backplate + .02);
        }
        translate([-( screw_spacing / 2 - spacing),0,0])
            cylinder(r = screw_radius, h = thickness_of_backplate + .02);
        translate([spacing*2 + ( screw_spacing / 2 - spacing),0,0])
            cylinder(r = screw_radius, h = thickness_of_backplate + .02);
    }
}

// shrouds

module shroud() {
    difference() {
        cylinder(r = diameter_of_signal/2 + buffer + shroud_thickness, h=shroud_height);
        translate([0,0,-0.01]) {
            cylinder(r = diameter_of_signal/2 + buffer, h=shroud_height + .02);
            *translate([0,-(diameter_of_signal/2 + buffer + shroud_thickness),0])
                cube([diameter_of_signal + buffer*2 + shroud_thickness*2, diameter_of_signal + buffer*2 + shroud_thickness*2, shroud_height + .02]);
        translate([-(diameter_of_signal/2 + buffer + shroud_thickness),diameter_of_signal/2 + buffer + shroud_thickness,shroud_height])
            rotate([90,45,0])
                cube([shroud_height*1.5,shroud_height,shroud_height]);
        }
    }
}

translate([spacing1, 0, thickness_of_backplate]) shroud();
translate([spacing2, 0, thickness_of_backplate]) shroud();
if (! aspect_count) {
    translate([spacing3, 0, thickness_of_backplate]) shroud();
}

```


[User]
I'm looking to design a spool holder for my 3D printer, and I'd like it to be parametric so I can adjust it later if needed. I'm using a Z18 printer, and I'm envisioning a design that mounts to the side of the printer. I'm thinking of using a 608ZZ bearing for smooth rotation of the spool, and an M8 threaded rod as the main axis.

Here's what I have in mind:

1.  **Overall Structure:** The holder should have two main parts: a mounting clip and a bearing support. The mounting clip will attach to the Z18 printer frame (which has a 4mm thick plate). I need two mounting clips that are mirror images of each other, and I would want to have the option to generate just the right or left side piece, or both. The mounting clip should have a channel to help slide it into place.

2.  **Mounting Clip Details:** The mounting clips will use a two-part design with a space in between the plates for the Z18 frame. It should have a rounded cutout to allow it to clip securely on the frame. The mounting clips need to be angled at 45/2 degrees (22.5 degrees) from the vertical, with each side angled in opposite directions.
3.  **Bearing Support Details:** The bearing support will hold the 608ZZ bearing. The bearing support should also have a hole for the M8 threaded rod, and the M8 hole should extend beyond the bearing to allow for the nuts to be placed on the thread. The support should have a flat rectangular base for mounting. The bearing will be oriented such that the axis of rotation is horizontal. This bearing support will mount to a vertical post attached to the mounting clip.

4.  **Dimensions:**
    *   608ZZ Bearing: I'm using a standard 608ZZ bearing which is approximately 8mm thick and 22.5mm in diameter.
    *   M8 Thread: I'm using M8 threaded rod, which has a diameter of about 12mm if you take into account the threads.
    *   Z18 Plate Thickness: The printer frame is 4mm thick.
    *   Mounting Clip Thickness: I would like the main plates of the mounting clip to be 6mm each, with a 6mm gap for the Z18 plate.
    *   Bearing Support Base dimensions: The base that mounts to the support should be a rectangle that is 20mm wide and 5mm deep. The vertical thickness of this rectangular section should be 3mm. The height should be determined by the bearing and the 20mm rectangular section.
    *   Spool Height: The height from the base of the mounting clip to the bottom of the bearing support needs to be 150mm
    *   The dimensions of the mounting clip should be slightly wider than the bearing support so that it does not interfere with the printer itself. It should be 20mm wider on each side of the center and have the same depth and height as the bearing support.

5.  **Parametric Design:** I would like the OpenSCAD file to allow for adjusting the following parameters:
    *   `Bearing608zzThick`
    *   `Bearing608zzDiameter`
    *   `M8ThreadDiameter`
    *   `XWallThick` (thickness of the width of the mounting clip)
    *   `YWallThick` (thickness of the depth of the mounting clip)
    *   `ZWallThick` (thickness of vertical part of the bearing support)
    *    `Z18plateThick`
    *   `spoolHeight`
    *   `part` (a string variable: "left", "right", or "both") to generate just one side or both of the spool holder.

Could you provide the OpenSCAD code for this? I don't need any additional libraries other than the standard OpenSCAD library.

[Assistant]
Z18SpoolHolder1.scad:
```
Bearing608zzThick = 8;
Bearing608zzDiameter = 22.5;
M8ThreadDiameter = 12;
XWallThick = 20;
YWallThick = 5;
ZWallThick = 3;

Z18plateThick = 4;
spoolHeight = 150;
part = "both"; // [left:left only,right:right only,both:left and right]

module bearingSupport(Bearing608zzThick, Bearing608zzDiameter, M8ThreadDiameter, XWallThick, YWallThick, ZWallThick)  {
    difference() {
        translate([-(XWallThick*2+Bearing608zzDiameter)/2, -(YWallThick*2+Bearing608zzThick)/2, 0]) {
            cube([XWallThick*2+Bearing608zzDiameter, YWallThick*2+Bearing608zzThick, ZWallThick + Bearing608zzDiameter*2/3]);
        }
        translate([0,0,ZWallThick+Bearing608zzDiameter/2]) {
            rotate([90,0,0]) {
                cylinder(r=Bearing608zzDiameter/2, h=Bearing608zzThick, $fn=100, center=true);
                cylinder(r=M8ThreadDiameter/2, h=Bearing608zzThick+YWallThick*2+2, $fn=100, center=true);
            }
        }
    }
}

module Z18clip(width, height, thick, Z18plateThick) {
    difference() {
        union() {
            translate([-(width)/2, -thick-Z18plateThick/2, 0]) {
                cube([width,thick,height]);
            }
            translate([-(width)/2, Z18plateThick/2, 0]) {
                cube([width,thick,height]);
            }
        }
        translate([0,-(thick*2+Z18plateThick+2)/2, height-32]) {
            rotate([-90,0,0]) {
                cylinder(h=thick*2+Z18plateThick+2, r=2, $fn=25);
            }
        }
    }

}

/*
 leftOrRight: 1 - left, (-1) - right
*/
module spoolHolder(leftOrRight) {
    difference() {
        translate([0,0,(XWallThick*2+Bearing608zzDiameter)/2*cos(45/2)-Z18plateThick/2*sin(45/2)]) {
            rotate([0,90,0]) {
                rotate([0,0,leftOrRight*45/2]) {
                    Z18clip(XWallThick*2+Bearing608zzDiameter, 45, 6, Z18plateThick);
                    translate([-(XWallThick*2+Bearing608zzDiameter)/2, -(6*2+Z18plateThick)/2, 45]) {
                        cube([XWallThick*2+Bearing608zzDiameter, 6*2+Z18plateThick, spoolHeight]);
                    }
                }
                rotate([0,0,(-leftOrRight)*45/2]) {
                    translate([0,0,45+spoolHeight]) {
                        bearingSupport(Bearing608zzThick, Bearing608zzDiameter, M8ThreadDiameter, XWallThick, YWallThick, ZWallThick);
                    }
                }
            }
        }
        translate([-300,-300,-300]) {
            cube([600,600,300]);
        }
    }
}

if (part == "left") {
    spoolHolder(-1);
} else if (part == "right") {
    spoolHolder(1);
} else if (part == "both") {
    translate([0, -(YWallThick*2+Bearing608zzThick)*1.5, 0]) {
        spoolHolder(1);
    }
    translate([0, +(YWallThick*2+Bearing608zzThick)*1.5, 0]) {
        spoolHolder(-1);
    }
}

```


[User]
Okay, I'm looking to create a custom shadow display, like the one described in that YouTube video.  Basically, it's a 3D printed object that shows one image when lit from the left and another image when lit from the right.  I have the two 20x20 pixel black and white images I want to use, and I'd like to be able to customize the final size and resolution.

So, I need an OpenSCAD file (or files) that can generate this. Here's what I'm thinking it needs to do:

1.  **Input Images:** It should take two 20x20 arrays representing the left and right images.  I guess these would be defined as OpenSCAD arrays.  I can copy and paste these in. I already have them defined in my current SCAD file, so I can copy that into your new file. For example:
```
left_image = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
right_image = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
```
2.  **Customizable Size:** I'd like to be able to adjust the overall size of the object, as well as the "pixel" size.  I think I can just multiply each pixel's dimensions by the nozzle size of the printer, but it would be nice to expose that as a parameter too.
3.  **Wall Structure:**  The design should create walls between pixels. The wall width should be adjustable. It doesn't need to be the full z-height. The thickness of the walls should be a parameter as well, but it might be good if the number of walls per pixel and the wall width were tied together. Let's say the number of 'sections' per pixel should be a parameter, and the wall width would be 1 section wide. So if you have 20 sections per pixel, the wall width would be 1/20th of a pixel wide.
4.  **Z-Height:** The difference in height between the high and low parts of the pixel should be also adjustable using a z-scale factor.
5.  **Base:**  There should be an extra base height below the main pixelated part of the object, again, adjustable.
6. **Shape**: It would be nice if the whole object could be either a circle or square. Let's call it 'overall_shape' and it should be a string which is either 'circle' or 'square'.

Essentially, I want to be able to create a wide range of these shadow displays, from small, high-resolution ones to larger, more basic ones. I want to be able to use the same image arrays, but change the final shape, size, and feature size of the pixel features.

Could you generate the OpenSCAD files for this? I think that's all I need for now. I'm not too familiar with the OpenSCAD libraries, but if there are any that would make this easier, I'm open to suggestions.

[Assistant]
shadow_display.scad:
```
// Width in mm. If you don't know then leave it at 0.4mm.
printer_nozzle_size = 0.4; //[0.2,0.3,0.4,0.6,0.8]

left_image = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; //  [image_array:20x20]
right_image = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,0,1,1,1,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; //  [image_array:20x20]

/* [Hidden] */
// This gives you that beautiful circular crop. Always times out in the web UI.
overall_shape = "square"; //[circle,square]

// Resolution
pattern_width = 20;
pattern_height = 20;
// Extra plastic at the bottom.
base_height = 40;
// This many blank pixels are added to left. right, top, bottom.
border = 2;
// Should pixels be black or white?
border_fill = 0;

// Convert to B&W
right = invert_and_bin(right_image);
left  = invert_and_bin(left_image);

// This is used to calculate how thick the type 3 walls are. _|_.
// 20 means that the wall is 2/20 = 0.1 pixels wide.
// Lowering this number will allow you to increase the resolution at the cost of thick (ugly) walls.
// This number must be even.
unit_width = 20;

// How high are the ledges in comparison to the pixel size? Basically z scale.
z_step_height = unit_width/1.5;

/*
Transition types:
_____ 0

__|-- 1

--|__ 2

__|__ 3

*/
function get_transitions(start, end, right, left) =
   [for (i = [start:end+1])
   let (
       a = i==start ? 0 : left[i-1],
       b = i>=end ? 0 : right[i]
   )
     a == 0 && b == 0 ? 0
   : a == 1 && b == 0 ? 1
   : a == 0 && b == 1 ? 2
   : 3];
function repeat(value, count) =
    [for(i = [0:count-1]) value];
// inverts color and converts anything to B&W
function invert_and_bin(pattern) =
    [for(i = [0:len(pattern)-1]) round(1 - pattern[i])];

/* Sections:

   1     2 3      4
________|- -|________
__________|----------
_____________________
*/
function get_heights(
   unit_width, transitions, i, height
   ) =
   i >= len(transitions) - 1 ? [] :
   let (
        transition = transitions[i],
        section_1_height = height,
        section_2_height = height +
            (transition == 0 ? 0
            :transition == 1 ? 0
            :transition == 2 ? 0
            : 1),
        section_3_height = height +
            (transition == 0 ? 0
            :transition == 1 ? 1
            :transition == 2 ? -1
            : 1),
        section_4_height = height +
            (transition == 0 ? 0
            :transition == 1 ? 1
            :transition == 2 ? -1
            : 0)
   )
   concat(
    repeat(section_1_height, unit_width/2 - 1),
    section_2_height,
    section_3_height,
    repeat(section_4_height, unit_width/2 - 1),

    get_heights(unit_width, transitions, i+1, section_4_height)
   );

function add_border(pattern, pattern_height, pattern_width, border, border_fill) =
  [for (row=[0-border:pattern_height-1+border])
    for (col=[0-border:pattern_width-1+border])
        row < 0 ? border_fill
        : row >= pattern_height ? border_fill
        : col < 0 ? border_fill
        : col >= pattern_width ? border_fill
        : pattern[row*pattern_height+col]
    ];

function get_surface(right, left, pattern_height, pattern_width, z_step_height) =
[for (row=[0:pattern_height-1])
    let (
        start = row*pattern_width,
        end = start + pattern_width,
        transitions = get_transitions(start, end, right, left),
        heights = get_heights(unit_width, transitions, 0, 0),
        center = (min(heights) + max(heights))/2
    )
    [for (col=[0:len(heights)-1])
        (heights[col]-center)*z_step_height
    ]];

function min_of_surface(surface) =
    min([for (row=[0:len(surface)-1]) min(surface[row])]);

function normalize_surface(base_height, surface) =
    let (
        min_height = min_of_surface(surface)
    )
    [for (row=[0:len(surface)-1])
        [for (col=[0:len(surface[row])-1])
            surface[row][col] - min_height + base_height
    ]];

module draw_object() {
    bordered_right = add_border(
        right,
        pattern_height,
        pattern_width,
        border,
        border_fill);
    bordered_left = add_border(
        left,
        pattern_height,
        pattern_width,
        border,
        border_fill);
    surface = get_surface(
        bordered_right,
        bordered_left,
        pattern_height+2*border,
        pattern_width+2*border,
        z_step_height);
    normalized_surface = normalize_surface(
        base_height,
        surface);

    for (row=[0:len(normalized_surface)-1])
        for (col=[0:len(normalized_surface[row])-1])
            translate(
                [col*printer_nozzle_size,
                row*printer_nozzle_size*unit_width,
                0])
            cube(
                [printer_nozzle_size,
                printer_nozzle_size*unit_width,
                normalized_surface[row][col]*printer_nozzle_size]);
}

if (overall_shape == "circle") {
    radius = (2*border + pattern_height)*unit_width*printer_nozzle_size/2;
    intersection() {
    translate([radius, radius, -radius]) cylinder(h=radius*10, r=radius, center=false, $fn=100);
    draw_object();
}}
else if (overall_shape == "square") {
    draw_object();
}

```


[User]
I want a customizable spiral pattern generator in OpenSCAD.  I'd like to be able to control the following:

*   **Resolution:** This will control the number of segments in the circles, and I'd like to define it as a single variable named `Resolution`.
*   **Amplitude:** This sets the overall size of the spiral.  Let's call this `Amplitude`.
*  **Bars:** This variable, `Bars`, will control how many individual bars are in the spiral pattern.
*   **Rotation Degrees:** The total rotation of the spiral, in degrees. I'll use the variable name `Rotation_Degrees` for this.
*   **Constant X Coefficient:** I need a coefficient to control the x-dimension of the rectangular bars. I'll call this `Constant_X_C`.
*   **Constant Y Coefficient:** Similar to the previous one but to control the y-dimension of the bars. Variable name should be `Constant_Y_C`.
*   **Forward:** This value controls the x offset of the elements on the first part of the spiral. The parameter name should be `Forward`.
*   **Backward:** This value controls the y offset of the elements on both parts of the spiral. The parameter name should be `Backward`.
*   **Thickness:** I need to control the height of the spiral elements. Name this variable `Thickness`.
*   **Scale X:** Allows scaling along the X axis.  I'll use the variable `Scale_X`.
*   **Scale Y:** Allows scaling along the Y axis. I'll use the variable `Scale_Y`.
*   **Scale Z:** Allows scaling along the Z axis. I'll use the variable `Scale_Z`.
*   **Double:** A boolean flag (true/false) to optionally create a second, mirrored spiral on top of the original.  Variable name will be `Double`.

The spiral should be made by combining a series of cylinders and cubes. The cylinders should have a radius that changes over the spiral and the cubes will have changing size too.
The structure should have an initial part where the radius and size of the bars increases and a second part where the radius and size of the bars decreases.
The rotation for each segment must increase linearly with each iteration.
I will need to be able to control the offset of the single components and the relative size through the parameters given.

Please do not use any external library for this project.

[Assistant]
Spiral_customizer.scad:
```
Resolution=36;  //AHSKJDHJDBDKJBDKJ

Amplitude=10;

Bars=100;

Rotation_Degrees=360;

Constant_X_C=20;

Constant_Y_C=1;

Forward=40;

Backward=70;

Thickness=2;

Scale_X=15;

Scale_Y=1;

Scale_Z=1;

Double=false;

$fn=Resolution;

b=Amplitude;            //Ampiezza

c=Bars;      //Inizio
d=Rotation_Degrees;       //Gradi giro

kxc=Constant_X_C;          //Coeffieciente divisione x del cubo
kyc=Constant_Y_C;          //Coeffieciente divisione y del cubo
x=Forward;
y=Backward;

spessore=Thickness;
scalex=Scale_X;
scaley=Scale_Y;
scalez=Scale_Z;

difference()
    {
    union()
        {
        for(a=[b/c:b/c:b])
            {
            if(a<b/2)
            rotate([0,0,d/b*a])
            translate([a*x,a*y,0])
             rotate([0,0,a])
              scale([scalex,scaley,scalez])
                {
               cylinder(r=a,h=spessore,center=true);
                rotate([0,0,0])
               cube([a*kxc,a/kyc,spessore],center=true);
                }
            if(a>=b/2)
            rotate([0,0,d/b*a])
            translate([(b-a)*x,a*y,0])
             rotate([0,0,a])
              scale([scalex,scaley,scalez])
                {
               cylinder(r=b-a,h=spessore,center=true);
                rotate([0,0,0])
               cube([(b-a)*kxc,(b-a)/kyc,spessore],center=true);
                }
            }
if(Double==true)
rotate([0,0,180])
for(a=[b/c:b/c:b])
            {
            if(a<b/2)
            rotate([0,0,d/b*a])
            translate([a*x,a*y,0])
             rotate([0,0,a])
              scale([scalex,scaley,scalez])
                {
               cylinder(r=a,h=spessore,center=true);
                rotate([0,0,0])
               cube([a*kxc,a/kyc,spessore],center=true);
                }
            if(a>=b/2)
            rotate([0,0,d/b*a])
            translate([(b-a)*x,a*y,0])
             rotate([0,0,a])
              scale([scalex,scaley,scalez])
                {
               cylinder(r=b-a,h=spessore,center=true);
                rotate([0,0,0])
               cube([(b-a)*kxc,(b-a)/kyc,spessore],center=true);
                }
            }
        }
    }
```


[User]
I need a customizable strap buckle for my swimming goggles. The original one broke, and I'd like to be able to generate replacements of varying sizes.

The buckle needs to have two slots for the strap to pass through, and the basic shape should be a rounded rectangle. The design I was working with had the following variables which determined the dimensions of the buckle: `strap_width`, `strap_depth`, and `total_height`. The buckle's overall width is calculated from `strap_depth`, and the total depth is derived from `strap_width`. The height is pretty straightforward. I'd like a similar system of parameters that I can adjust.

The buckle should be generated using an OpenSCAD file that allows me to set these strap dimensions and desired thickness as parameters. The rest of the dimensions should be calculated from those values. The generated buckle should have rounded ends. I think I did this with two spheres intersecting with the main block in my old version, but it's probably possible to achieve this with `hull()` or `rounded_cube()` or something from one of the libraries. I'd prefer to use libraries where possible.

I'd like the OpenSCAD file to be parametric, so I can easily change the strap dimensions and total thickness. I'd also like it to be relatively easy to understand. I'm not super familiar with all the different approaches, but I like the idea of having all my parameters at the top.

I'd like the final OpenSCAD file to generate just the buckle with all the cuts necessary to fit a given strap. I think that `BOSL2` would be helpful for this, specifically if it has some functions for generating rounded cubes or cylinders. I'd like to avoid hardcoding any numbers that can be easily derived from `strap_width`, `strap_depth`, and `total_height`.

[Assistant]
strap-buckle-customizable.scad:
```
// strap buckle customizable
// speedo goggles w=10.3 d=1.9 h=1.8

strap_width=10.3; // mm
strap_depth=1.9; // mm

total_width=strap_depth*2+strap_depth*2+strap_depth*1.5+strap_depth*2+strap_depth*2;
total_depth=strap_width+5;
total_height=1.8; // mm
t=0.2*1; // tolerance for holes

intersection()
{
    difference()
    {
        // main buckle block
        cube([total_width,total_depth,total_height], center=true);
        // holes for strap
        translate([strap_depth*1.75,0,0])
            cube([strap_depth*2+t,strap_width+t,total_height+t],center=true);
        translate([-strap_depth*1.75,0,0])
            cube([strap_depth*2+t,strap_width+t,total_height+t],center=true);
    }

    // intersection to shape rounded borders with two spheres
    translate([pow(total_depth,1.2)-total_width/2,0,0])
        sphere(pow(total_depth,1.2),$fn=60);
    translate([-pow(total_depth,1.2)+total_width/2,0,0])
        sphere(pow(total_depth,1.2),$fn=60);
}

```


[User]
I'd like to create a customizable barrel-shaped planter, and I'd like to be able to adjust quite a few parameters to get it just right. I think I'd like this as a single file that allows me to specify whether I want just a pot, just a saucer, or both.

Here are the parameters I need:

*   **barrelWidth:** The width of the barrel in millimeters.
*   **barrelHeight:** The height of the barrel in millimeters.
*   **wall:** The wall thickness in millimeters.
*   **roundness:**  A value to control the vertical roundness (a percentage) - I want the vertical curve to be elliptical.
*   **drainHole:** A yes/no option to include a drain hole.
*   **hole:**  The diameter of the drain hole in millimeters.
*   **barrelHoops:** A yes/no option to include barrel hoops.
*    **hoopWall:** The thickness of the barrel hoops.
*   **potOrSaucer:** An option to choose whether to create just the pot, just the saucer, or both.

I'd like the barrel hoops to be evenly spaced along the barrel height, and for the saucer to have a raised ring with some reinforcing struts on the bottom. When generating both a pot and a saucer, I'd prefer them to be next to each other.

Could you generate the OpenSCAD file for this? It shouldn't rely on any external libraries.

[Assistant]
roundish-3.scad:
```
// preview[view:south, tilt:top]
/* [simple settings] */
// width of barrel in mm
barrelWidth = 63; //[30:5:300]
// height of barrel in mm
barrelHeight = 75;   //[30:5:300]
// wall thickness in mm
wall = 1.2; //[.6:.1:10]
// vertical roundness
roundness = 55;   //[20:80]
// drain hole
drainHole = "yes";   //[yes, no]
// drain hole diameter in mm
hole = 10;  //[8:1:25]
// barrel hoops
barrelHoops = "yes"; //[yes, no]
// barrel hoop thickness in mm
hoopWall = .9; //[.2:.1:5]
// pot or saucer
potOrSaucer = "pot"; //[pot, saucer, both]

/* [hidden] */
x = 0; y = 1; z = 2;
pot =[barrelWidth, barrelWidth, barrelHeight];
elongate = 1. - roundness/100.;
hoop = [pot[x]+2*hoopWall, pot[x]+2*hoopWall, barrelHeight/10];
yy = [1,2,4,5,7,8];
lores = 80;
hires = 80;
$fn = hires;
allow = ( barrelHoops == "yes" ) ? 4 + 2* hoopWall : 4;
minSaucer = 12;
dish = [pot[x]+allow, pot[x]+allow, max(pot[z]/10, minSaucer)];

module barrel(xx, zz, res) {
   difference() {
      translate([0, 0, zz/2])
      scale([1, 1, (zz/xx)*(1+elongate)])
         sphere(d=xx, $fn=res);
      union() {
         translate([-xx/2, -xx/2, -zz*elongate/2])
            cube([xx, xx, zz*elongate/2]);
         translate([-xx/2, -xx/2, zz])
            cube([xx, xx, zz*elongate/2]);
      }
   }
}
module pot() {
   difference() {
      // main
      union() {
         barrel(pot[x], pot[z]+wall, lores);
         // hoops
         if ( barrelHoops == "yes" ) {
            difference() {
               barrel(hoop[x], pot[z]+wall, hires);
               union() {
                  for ( i=[0:1:len(yy)-1] ) {
                     translate([-hoop[x]/2, -hoop[x]/2, yy[i]*hoop[z]])
                        cube([hoop[x], hoop[x], hoop[z]]);
                  }
               }
            }
         }
      }
      // hollow and drain hole
      union() {
         translate([0, 0, wall])
            barrel(pot[x]-2*wall, pot[z]-wall, lores);
         translate([-pot[x]/2, -pot[x]/2, pot[z]-.0001])
            cube([pot[x], pot[x], 2*wall]);
         translate([0, 0, -1.5*wall])
            cylinder(h=3*wall, d=hole);
      }
   }
}
module saucer() {
   difference() {
      // main
      barrel(dish[x], pot[z], hires);
      // hollow
      union() {
         translate([0, 0, wall])
            barrel(dish[x]-2*wall, pot[z]-wall, hires);
         translate([-dish[x]/2, -dish[x]/2, dish[z]])
            cube([dish[x], dish[x], pot[z]]);
      }
   }
   // ring
   translate([0, 0, wall])
      difference() {
         cylinder(h=wall, d=.5*dish[x]);
         union() {
            cylinder(h=2*wall, d=.5*dish[x]-2*wall);
            for ( angle = [0:45:135] ) {
               rotate([0, 0, angle])
                  cube([wall, dish[x], 2*wall], center=true);
            }
         }
      }
}
if ( potOrSaucer == "pot" ) {
   pot();
}
else if ( potOrSaucer == "saucer" ){
   saucer();
}
else if ( potOrSaucer == "both" ){
   pot();
   translate([(pot[x]+dish[x])/2+3, 0, 0])
      saucer();
}

```


[User]
I'd like to design a customizable squeegee, and I need some help with the OpenSCAD code. I want it to be parametric so I can easily adjust the dimensions.

The main components are the squeegee head and the blade. The head should have a threaded hole to fit a standard 3/4-5 ACME broom handle. I'm hoping to use the Fscrew.stl file from Thingiverse (thing:8478) for that screw thread, which I understand is licensed under CC-BY-NC. I also understand that this will be included in the final STL export and that I will need to add this .stl using my preferred STL editing program.

The blade needs to be designed to be printed with flexible filament, and I'd like to control its width, length, and thickness. I'd also like to adjust the length of the handle part of the head, as well as the angle it makes with the blade. Additionally, I'd like to be able to set how much the blade extends beyond the sides of the head (the overhang).

I want to be able to customize the following parameters in the Customizer:

*   **Blade:**
    *   `width`
    *   `length`
    *   `height` (thickness)
*   **Head:**
    *   `angle` (angle of the handle relative to the blade)
    *   `head_width`
    *   `handle_length`
    *  `overhang` (how much blade extends beyond the head)
*   **Display:**
    *   `show_build_plate` (a yes/no toggle to display a build plate; default is "yes")
    *   `build_plate_selector` (a dropdown to choose common build plate sizes)
    *   `build_plate_manual_x` (custom x dimension for the build plate)
     *   `build_plate_manual_y` (custom y dimension for the build plate)
    *   `show` (a dropdown to select to show either 'all', the 'blade', or the 'head')
*   **Other (hidden):**
    *   `EPSILON` (a small number for tolerances, set to 0.01)
    *   `build_plate_rotate` (an angle to rotate the build plate by)
    *   `notch` (proportion of the `head_width` to make a notch for the blade)
    *    `gap` (tolerance between the blade and the head; default to 0.2)

I want a "notch" in the head where the blade fits, and the blade should fit with the specified `gap` tolerance. I'd also like the ability to show only the blade, only the head, or both in the Customizer output. I will also need to be able to output without the build plate (toggle with `show_build_plate`).

I'm already using the `dotSCAD` library (specifically `build_plate.scad`), so please make sure that is compatible.

Could you generate the OpenSCAD code for this?

[Assistant]
squeegee_1.0.scad:
```
/**
* A parametric, customizable squeegee head and blade
*
* @author Brian Alano
* @version 1.0
* @serial (or @serialField or @serialData)
* @license Creative Commons - Attribution - Non-Commercial 4.0 https://creativecommons.org/licenses/by-nc/4.0/legalcode
* @copyright 2018 Green Ellipsis LLC. www.greenellipsis.org.

Included Libraries
------------------
dotSCAD libraries are licensed under the LGPL. [![license/LGPL](https://img.shields.io/badge/license-LGPL-blue.svg)](https://github.com/JustinSDK/lib-openscad/blob/master/LICENSE)

 Fscrew.stl from https://www.thingiverse.com/thing:8478/files licensed under CC-BY-NC https://creativecommons.org/licenses/by-nc/4.0/legalcode

The squeegee head uses the female screw thread from [Zydac](https://www.thingiverse.com/thing:8478/files) to fit a 3/4-5 ACME broom handle screw. Squeegee blade should be printed from flexible filament. Using 3D Solutech Flexible filament, 3 mm thick was just a little too thick for wiping windows.

Features include:
* Customizable blade thickness, width, handle length and handle angle
* Works in Makerbot/Thingiverse Customizer
* Works in OpenSCAD Customizer
* Parametric design

To design your own thing in OpenSCAD
-------------------------------
1. Download and extract the .zip file.
2. Install the Development version of OpenSCAD (http://www.openscad.org/downloads.html#snapshots)
3. Enable the Customizer according to https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/WIP#Customizer
4. Open the .scad file
5. Preview the model (Design-->Preview)
6. Pick your preset or set your customizations
7. Render (Design-->Render)
8. Export as an STL file (File-->Export)

Get more Customizer help from DrLex: https://www.thingiverse.com/thing:2812634.

*/

use <utils/build_plate.scad>;

/* [Build Plate] */
//for display only, doesn't contribute to final object
build_plate_selector = 0; //[0:Replicator 2,1: Replicator,2:Thingomatic,3:Manual]

//when Build Plate Selector is set to "manual" this controls the build plate x dimension
build_plate_manual_x = 100; //[100:400]

//when Build Plate Selector is set to "manual" this controls the build plate y dimension
build_plate_manual_y = 100; //[100:400]

/* [Parts] */
show_build_plate="yes"; //[yes,no]
//parts to show
show="both"; // [all,blade,head]

/* [Blade] */
width=60;
length=200;
height=5;

/* [Head] */
angle=30;
head_width=40;
handle_length=25;
// how much the blade extends beyond the head on the sides
overhang=2;
// proportion of head_width to make into a notch
notch=0.5;
// tolerance between the blade and the head
gap=0.2;

/* [Hidden] */
EPSILON=0.01;
build_plate_rotate = 45; //[0:360]
holder_length=length-overhang*2;

show_assembled=(show_build_plate=="yes");
show_blade=(show=="all" || show=="blade");
show_head=(show=="all" || show=="head");

// positioning of blade in assembled model. Also used to cut notch
blade_translation=[width/2+head_width/2-head_width*notch,0,height*3/2];

module channel(cut, length) {
	cylinder(d=cut, h=length+EPSILON*2, $fn=3, center=true);
}

module channels(extra=false) {
	Cut=height/3;
	Increment=height/2;
	Start=height;
	translate([-width/2, 0, 0]) {
		for (i=[Start:Increment:Start+Increment*2]) {
			translate([i, 0, height*0.43]) rotate([90,-30,0])
				channel(cut=Cut, length=length);
			translate([i+Increment/2, 0, -height*0.43]) rotate([90,30,0])
				channel(cut=Cut, length=length);
		}
		if (extra) translate([Start-Increment/2, 0, -height*0.43]) rotate([90,30,0])
				channel(cut=Cut, length=length);
	}
}

module blade(length, width, height, extra=true) {
	module additive() cube([width, length, height], center=true);

	module subtractive() channels(extra);

	difference() { additive(); subtractive(); }
}

module shaft() {
	StudLength=26;
	StudR=13;
	HandleLength=max(StudLength, handle_length);
	StudInset=StudLength-3;
	Translation=[-head_width/2+abs(sin(angle))*height*3, 0, abs(sin(angle))*height];
	module additive() {
		hull() {
			cube([head_width, holder_length, height*3], center=true);
			translate(Translation) rotate([0, -angle, 180])
				translate([HandleLength, 0, 0]) rotate([0,-90,0]) cylinder(r=StudR, h=EPSILON);
		}
		hull() {
			cube([head_width, holder_length, height*3], center=true);
			translate(Translation) rotate([0, -angle, 180])
			translate([HandleLength-StudLength, 0, 0]) rotate([0,-90,0]) cylinder(r=StudR, h=EPSILON);
		}
	}

	module subtractive() {
		Height=height+gap*2;
		// use the blade profile to cut the notch
		translate([blade_translation.x, blade_translation.y]) blade(length*2, width, Height, extra=false);
		// then cut a little deeper to give some room
		translate([(1/2-notch)*head_width-Height/2+EPSILON,0,0]) rotate([90,0,0]) cube([Height, Height, length*2], center=true);
		translate(Translation)
			rotate([0, -angle, 180])
			translate([HandleLength+EPSILON, 0, 0]) rotate([0,-90,0])
			cylinder(r=11, h=StudInset);
	}

	module additive2() {
		translate(Translation)
			rotate([0, -angle, 180])
		translate([HandleLength, 0, 0]) rotate([0,-90,0])
			stud();
	}

	difference() { additive(); subtractive(); }
	additive2();
}

module stud() {
	import("Fscrew.stl");
}

module head() {
	module additive() {
			shaft(HandleLength);
	}

	module intersect() {
		Height=handle_length+height*5;
		translate([0, 0, Height/2-(height*3)/2]) cube([holder_length, holder_length, Height], center=true);
	}

	intersection() {
		additive();
		if (angle>=20) intersect();
	}
}

module main() {
	//translate([0, 0, length/2]) rotate([90,0,0])
	{
		if (show_blade) {
			color("gray")
//			if (show_assembled) {
				translate(blade_translation) blade(length, width, height);
//			} else {
//				translate([0, 0, height/2]) blade(length, width, height);
//			}
		}
		if (show_head) {
//			if (show_assembled) {
				translate([0,0,height*3/2]) rotate([0,0,0]) head();
//			} else {
//				head();
//			}
		}
		if (show_assembled) {
			rotate([0, 0, build_plate_rotate])
				build_plate(build_plate_selector,build_plate_manual_x,build_plate_manual_y);
		}
	}
}

main();

```


[User]
Hi, I'm looking for some OpenSCAD code to generate a set of parametric Vol-au-Vent cutters. I'd like to be able to choose between a few different shapes: circular, oval, regular polygons (with 3 to 12 sides), and rectangles. For each shape, I'd like to create two cutters – a "top" cutter and a "base" cutter. The base cutter is a simple outline, while the top cutter should create a wall with a specific width for the Vol-au-Vent, and a top lip.

Specifically, here are the parameters I'd like to control:

*   **Basic choices:**
    *   `shape_choice`: A dropdown/selector to choose the shape (Circle, Oval, Polygon, Rectangle).
    *   `vol_wall`: The width of the Vol-au-Vent wall (between the inner and outer cut of the top cutter).
    *   `cutter_depth`: The depth of the cutters.
    *   `cutter_wall`: The wall thickness of the cutter itself.
    *   `lip_dim`: The size of the top lip of the top cutter.
    *   `part`: A choice of which cutter to preview: top, base, or both.

*   **Circular cutter options (only if `shape_choice` is "Circle"):**
    *   `circ_dia`: The diameter of the circle.

*   **Oval cutter options (only if `shape_choice` is "Oval"):**
    *   `pill_len`: The overall length of the oval.
    *   `pill_dia`: The diameter of the ends of the oval.

*   **Polygon cutter options (only if `shape_choice` is "Polygon"):**
    *   `poly_faces`: The number of sides of the regular polygon.
    *   `poly_side`: The length of each side of the polygon.

*   **Rectangular cutter options (only if `shape_choice` is "Rectangle"):**
    *   `rect_wide`: The width of the rectangle.
    *   `rect_high`: The height of the rectangle.

I think that covers it.  Essentially I want a really flexible system for making these cutters.  I guess I should also mention, I'm using OpenSCAD version 2021.01 so please stick to that functionality set. Also, I'm not using any external libraries like MCAD or BOSL. Can you help me out?

[Assistant]
VolAuVent_cutter.scad:
```
// Customisable pastry cutter for
// blank shape and Vol-au-vent wall
//
// Options are circular, oval, polygonal (regular only) or rectangle
// square can be made by 4 sided polygon or rectangle with X=Y
//
// By misterC @ thingiverse

// CUSTOMIZER VARIABLES
/* [Basic choices] */
// Do you want a regular polygon, a circle, square/rectangle or an oval?
shape_choice = "Polygon";	// [Circle:Circular,Oval:Oval,Polygon:Regular polygon (3 to 12 sides),Rectangle:Rectangular]
//
// What width of Vol-au-Vent wall?
vol_wall = 18;			// [10:0.5:25]
// What depth of cutter?
cutter_depth = 25;		// [10,15,20,25,30]
// What wall thickness? (base this on your nozzle diameter)
cutter_wall = 1.2;		// [0.6,0.8,1.0,1.2,1.4,1.6,1.8,2]
// Top lip size?
lip_dim = 3;			// [3:6]
//
//Which cutter to show in preview
part ="both";	// [top:Upper cutter,base:Lower cutter,both:Both cutters]

/* [Circular cutter options] */
// What diamater should the cutter shape be?
circ_dia=75;		// [40:5:150]

/* [Oval cutter options] */
// What overall width should the cutter shape be?
pill_len = 150;		// [50:5:200]
// What diameter should the ends of the cutter be?
pill_dia = 90;		// [20:5:200]

/* [Polygon cutter options] */
// How many faces do you want?
poly_faces = 6;		// [3:12]
// and what length should each side be?
poly_side = 75;		// [50:5:150]

/* [Rectangular cutter options]*/
// What width should the cutter shape be?
rect_wide = 100;		// [40:5:160]
// What height should the cutter shape be?
rect_high = 75;			// [40:5:160]

// PROGRAM VARIABLES AFTER THIS POINT
/* [Hidden] */
// Calculated values
// centres & half_centre are the eccentricity dims for ovals, so can be zero for non ovals
centres = (shape_choice=="Oval") ?	pill_len - pill_dia : 0;
half_centre = (shape_choice=="Oval") ? centres/2 : 0;
out_rad = (shape_choice=="Circle") ? circ_dia/2
		: (shape_choice=="Oval") ? pill_dia/2
		: (shape_choice=="Polygon") ? poly_side/(2*sin(180/poly_faces))
		: 0;
in_rad = out_rad - vol_wall;
// offset dimension for showing both pieces
both_off = (shape_choice=="Circle" || shape_choice=="Polygon") ? 1.2*out_rad
		: (shape_choice=="Oval") ? 0.6*pill_len
		: 0.75*rect_wide;

// Shape values matrix (X,Y for rectangular otherwise radius)
shape_set = (shape_choice=="Rectangle") ? [
		[rect_wide,	rect_high],
		[rect_wide-2*vol_wall,	rect_high-2*vol_wall],
		[rect_wide+2*cutter_wall,	rect_high+2*cutter_wall],
		[rect_wide-2*(vol_wall+cutter_wall),	rect_high-2*(vol_wall+cutter_wall)],
		[rect_wide+2*lip_dim,	rect_high+2*lip_dim],
		[rect_wide-2*(vol_wall+lip_dim),	rect_high-2*(vol_wall+lip_dim)]
		] : [
		out_rad,
		in_rad,
		out_rad + cutter_wall,
		in_rad - cutter_wall,
		out_rad + lip_dim,
		in_rad - lip_dim
		];

// how many grips will we put onto cutter sides (or round a cutter radius)
// x & y for rectangle sides, c around curve of circular, o for straight of oval
// polygons get grips at corners, so we only need to know the angle
x_grip = (shape_choice=="Rectangle") ? get_a_grip(shape_set[1].x,2.3) : 0;
y_grip = (shape_choice=="Rectangle") ? get_a_grip(shape_set[1].y,2.3) : 0;
c_grip = (shape_choice=="Circle") ? get_a_grip(shape_set[1]*2*PI,4)
		: (shape_choice=="Oval") ? get_a_grip(shape_set[1]*PI,3) : 0;
c_ang = (shape_choice=="Circle") ? 360/c_grip
		: (shape_choice=="Oval") ? 180/c_grip : 0;
o_grip = (shape_choice=="Oval") ? get_a_grip(centres,3) : 0;
p_ang = (shape_choice=="Polygon") ? 360/poly_faces : 0;

$fn = (shape_choice=="Polygon") ? poly_faces : 60;		// Cheat at doing polygon, otherside reasonably smooth curves
fudge = 1;		// small offset to assist with difference() volumes

// PROGRAM - process which part(s) to show and call the necessary module(s)

if (part == "top")
{
	if (shape_choice=="Rectangle") rect_top();
	else top();
}
else if (part == "base")
{
	if (shape_choice=="Rectangle") rect_base();
	else base();
}
else
{
	translate([-both_off,0,0])
	{
		if (shape_choice=="Rectangle") rect_top();
		else top();
	}
	translate([both_off,0,0])
	{
		if (shape_choice=="Rectangle") rect_base();
		else base();
	}
}

// MODULES AND FUNCTIONS

module top()
{
	// outside shape, bulb facing out
	base();
	// inside shape, bulb facing in
	difference()
	{
		shape(1,cutter_depth);	// cutter body

		translate([0,0,-fudge]) shape(3,cutter_depth+2*fudge);	// inner shape
	}
	difference()
	{
		shape(1,lip_dim);		// bulb at top faces in

		translate([0,0,-fudge]) shape(5,lip_dim+2*fudge);
	}
	// grips between inner and outer shapes
	intersection()
	{
		difference()	// "donut" of top lip
		{
			shape(4,lip_dim);

			translate([0,0,-fudge]) shape(1,lip_dim+2*fudge);
		}

		translate([0,0,lip_dim/2]) union()		// all the webs
		{
			if (shape_choice=="Circle") for (ang=[0:c_ang:360]) poly_grip(ang,vol_wall);
			else if (shape_choice=="Polygon") for (ang=[0:p_ang:360]) poly_grip(ang,poly_side/3);		// big grip at corners
			else	// only oval left
			{
				translate([half_centre,0,0]) for (ang=[-90+c_ang:c_ang:90-c_ang]) poly_grip(ang,vol_wall);		// first curve
				translate([-half_centre,0,0]) for (ang=[90+c_ang:c_ang:270-c_ang]) poly_grip(ang,vol_wall);		// last curve
				rect_grips(0,o_grip,shape_set[4],centres);		// straights are like rectangle long side
			}
		}
	}
}

module base()
{
	difference()
	{
		union()
		{
			shape(4,lip_dim);			// bulb at top faces out
			shape(2,cutter_depth);	// cutter body
		}

		translate([0,0,-fudge]) shape(0,cutter_depth+2*fudge);	// inner shape
	}
}

module shape(num,depth)
{
	if (shape_choice=="Oval")
	{
		for (end=[-half_centre,half_centre]) translate([end,0,0]) cylinder(r=shape_set[num],h=depth);
		translate([-half_centre,-shape_set[num],0]) cube([centres,shape_set[num]*2,depth]);
	}
	else cylinder(r=shape_set[num],h=depth);
}

module poly_grip(rot_ang,what_grip)
{
	rotate(rot_ang) translate([shape_set[1],0,0]) cube([3*vol_wall,what_grip,lip_dim],center=true);
}

module rect_top()
{
	// outside shape, bulb facing out
	rect_base();
	// inside shape, bulb facing in
	difference()
	{
		rect_shape(1,cutter_depth,0);		// cutter body

		rect_shape(3,cutter_depth,2*fudge);	// inner shape
	}
	difference()
	{
		rect_shape(1,lip_dim,0);			// bulb at top faces in

		rect_shape(5,lip_dim,2*fudge);
	}
	// grips between inner and outer shapes
	intersection()
	{
		difference()	// "donut" of top lip
		{
			rect_shape(4,lip_dim,0);

			rect_shape(1,lip_dim,2*fudge);
		}

		translate([0,0,lip_dim/2]) union()		// all the webs
		{
			rect_grips(0,x_grip,shape_set[4].y,shape_set[1].x);
			rect_grips(90,y_grip,shape_set[4].x,shape_set[1].y);
		}
	}
}

module rect_base()
{
	difference()
	{
		union()
		{
			rect_shape(4,lip_dim,0);		// bulb at top faces out
			rect_shape(2,cutter_depth,0);	// cutter body
		}

		rect_shape(0,cutter_depth,2*fudge);	// inner shape
	}
}

module rect_shape(num,height,is_fudged)
{
	translate([0,0,height/2]) cube([shape_set[num].x,shape_set[num].y,height+is_fudged],center=true);
}

module rect_grips(rot_ang,what_grip,grip_high,length)
{
	rotate(rot_ang)
		if (what_grip==1) cube([vol_wall,2*grip_high,lip_dim],center=true);
		else
		{
			grip_gap = (length-(vol_wall*what_grip))/(what_grip-1);
			for (grip=[0:what_grip-1])
				translate([(grip*(grip_gap+vol_wall))+(vol_wall-length)/2,0,0]) cube([vol_wall,2*grip_high,lip_dim],center=true);
		}
}

function get_a_grip(length,multiple)	// returns an integer based on vol_wall and the length to support
= round(length/(multiple*vol_wall));
```


[User]
Hi, I'm looking to create a customizable filler cap for a kitchen blender, and I'd like to use OpenSCAD to design it. I want to make it parametric so I can adjust it for different blender models.

Essentially, the design has a cylindrical base with optional features on top and around the sides, and the ability to create a hollow or solid core. I've got some specific features in mind, which I will detail now.

First, there's a main cylindrical portion of the cap, whose dimensions I want to control by `outer_diameter` and `total_height`. I also need to be able to set the height from the bottom of the cap up to the rubber lid (or the point where the lip rests) via `bottom_height`. Additionally, the rubber lid's thickness at its innermost point can be specified by the `rubber_thickness` parameter.

Then, I'd like to add an optional top feature that is either absent or present. If present, its thickness is controlled by `top_thickness`, its radius by `top_radius`, and the roundness of its corners by `top_rounding_radius`. I’d also like to have an option to set the number of sides of the top, using the parameter `top_number_of_sides`.

Next, I want a lip feature above the rubber lid that is also optional. This lip will have an outer diameter `lip_diameter`, an outer thickness `outer_lip_thickness` and an inner thickness `inner_lip_thickness`.

I also need an optional latch feature to help hold the cap in place. These latches are rectangular and their dimensions can be controlled by `latch_width`, `latch_length`, and `latch_thickness`.

Finally, I need a way to create either a solid cap or a hollow one. If it's hollow, there will be an inner cylinder defined by `inside_diameter`, `inside_height` and a `inside_rounding_radius` for the top of the interior cylinder.

I need to be able to choose the units used via a parameter called `units`, with 25.4 for inches, 1 for mm, and 10 for cm.

Could you generate the OpenSCAD code for this design? I don't need it to be able to use Customizer - just a functional and clean OpenSCAD file. I don't expect any libraries to be needed. I'm happy to make any minor adjustments myself once it is generated.

[Assistant]
blenderTop.scad:
```
//	Filler Cap for Blender. Recommend printing using food safe material and process.
//	Copyright (C) 2018 Herb Weiner <herbw@wiskit.com>. Creative Commons by Attribution.

/* [General Settings] */
//	Unit of Measure for all Numeric Entries
units =						25.4;		//	[25.4:inches,1.0:mm,10.0:cm]
//	of Cylindrical Portion of Filler Cap
outer_diameter =			2.0;
//	of filler cap
total_height =				1.5;
//	Height Below Top of Rubber Lid
bottom_height =				0.75;
//	Thickness of Rubber Lid at Innermost Point
rubber_thickness =			0.2;

/* [Settings for Top of Cap] */
//	Present or Absent
top =						1;			//	[1:Top is Present,0:Top is Absent]
//	(of Top of Cap)
top_thickness =				0.1;
//	of Circumscribing Circle
top_radius =				1.35;
//	Radius of Rounded Corners
top_rounding_radius =		0.25;
//	Number of Sides on Top of Cap
top_number_of_sides =		6;			//	[3,4,5,6,7,8]

/* [Settings for Lip Above Rubber Lid] */
//	Present or Absent
lip =						1;			//	[1:Lip is Present,0:Lip is Absent]
//	of Outermost Portion
lip_diameter =				2.3;
//	Thickness of Outermost Portion of Lip above Rubber Lid
outer_lip_thickness =		0.06;
//	Thickness of Innermost Portion of Lip above Rubber Lid
inner_lip_thickness =		0.13;

/* [Settings for Rectangular Latches that Hold Cap in Rubber Lid] */
//	Present or Absent
latch =						1;			//	[1:Latch is Present,0:Latch is Absent]
//	from Side to Side
latch_width =				0.4;
//	from Cylinder to Outside
latch_length =				0.125;
//	from Top to Bottom
latch_thickness =			0.2;

/* [Interior Settings] */
//	Cap can be hollow with open bottom or solid
hollow =					1;			//	[1:hollow with open bottom,0:solid]
//	Only applies for Hollow Cap
inside_diameter =			1.75;
//	Only applies for Hollow Cap
inside_height =				1.25;
//	Radius for Rounding the Top of the Inside Cylinder (Only applies for Hollow Cap)
inside_rounding_radius =	0.2;

module GoAwayCustomizer ()
{
// This module is here to stop Customizer from picking up the variables below
}

delta =						0.01;
LipThicknessDelta =			inner_lip_thickness - outer_lip_thickness;

module Top ()
{
	if (top_rounding_radius > 0)
	{
		hull ()
		{
			for (i = [1:top_number_of_sides])
			{
				rotate ([0, 0, i * 360 / top_number_of_sides]) translate ([top_radius - top_rounding_radius, 0, 0])	cylinder (r = top_rounding_radius, h = top_thickness, $fn = 360);
			}
		}
	}
	else
	{
		cylinder (r = top_radius, h = top_thickness, $fn = top_number_of_sides);
	}
}

module Latches ()
{
	translate ([-(latch_width / 2), -(latch_length + (outer_diameter / 2)), total_height - bottom_height + rubber_thickness])
		cube ([latch_width, outer_diameter + (2 * latch_length), latch_thickness]);
}

module Lip ()
{
	translate ([0, 0, total_height - bottom_height - inner_lip_thickness])
		cylinder (d = lip_diameter, h = outer_lip_thickness, $fn = 360);
	if (inner_lip_thickness > outer_lip_thickness)
	{
		translate ([0, 0, total_height - bottom_height - LipThicknessDelta])
			cylinder (d1 = lip_diameter, d2 = outer_diameter, h = inner_lip_thickness - outer_lip_thickness, $fn = 360);
	}
}

module Cap ()
{
	cylinder (d = outer_diameter, h = total_height, $fn = 360);
}

module outside ()
{
	Cap ();
	if (top)
		Top ();
	if (lip)
		Lip ();
	if (latch)
		Latches ();
}

module inside ()
{
	if (hollow != 0)
	{
        translate ([0, 0, total_height - inside_height + inside_rounding_radius])
		{
			if (inside_rounding_radius > 0)
			{
				minkowski ()
				{
					cylinder (d = inside_diameter - 2 * inside_rounding_radius, h = inside_height - inside_rounding_radius, $fn = 360);

					sphere (inside_rounding_radius, $fn = 100);
				}
			}
			else
				cylinder (d = inside_diameter, h = inside_height + delta, $fn = 360);
		}
	}
}

scale (units)
difference ()
{
	outside ();
	inside ();
}

```


[User]
Okay, I need an OpenSCAD design for a custom blower turbine, kind of like the one described in that blog post I saw. I'm thinking of using it to replace the broken one in my shop vac.

The turbine should be based on involute curves, like in the example, and have a parametric design, so I can tweak it to fit different motors and housings if needed. The basic structure should be a rotor with a top and bottom plate, and vanes in between.

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

*   **`total_height`**: The overall height of the assembled turbine.
*   **`plate_thickness`**: The thickness of both the top and bottom plates.
*   **`outside_dia`**: The overall diameter of the rotor.
*   **`intake_dia`**: The diameter of the intake hole in the top plate.
*   **`num_vanes`**: The number of vanes in the turbine.
*  **`vane_thickness`**: The thickness of each vane.
*   **`shaft_dia`**: The diameter of the center hole for the motor shaft.
*   **`vane_start_dia`**: The diameter at which the vanes start, using `intake_dia` if not specified (or if set to < 0).
*   **`direction`**: A way to control if the vanes go clockwise or counter-clockwise.
*   **`vane_fit`**: A small gap allowance for how snugly the top plate fits with the vanes; I’m thinking a small value like 0.25.
*   **`resfactor`**: Something to tweak the resolution for generating the curves, so things don’t get too slow. I think a value of 2 or 3 would be best.

I'd like the design to be modular, so I can render the whole assembled turbine, or just the top or bottom separately for 3D printing. I'm envisioning a variable `show` that will take a value of `0` to show everything assembled, `1` for just the top piece, and `2` for just the bottom.

And finally, if you could include the involute functions, as well as the functions to sweep objects along the curves, like in the example, that would be amazing.  I don't think I will need the archimedes related modules, but it might be useful to include it if its not too much extra work.

I think that's everything! Let me know if you have any questions.

[Assistant]
involute_impeller.scad:
```
// Create a blower turbine based on involute curves
//    https://en.wikipedia.org/wiki/Involute
//
// This is licensed under Creative Commons+Attribution+Share Alike
//
// To fulfill the attribution requirement, please link to one or both of:
//   https://theheadlesssourceman.wordpress.com/2018/06/24/custom-blower-turbine
//   https://www.thingiverse.com/thing:2976678

// what part to show
show=0; // [0:assembled,1:printable top,2:printable bottom]

// the total, complete, outside height
total_height=11.3;

// thickness of the top and bottom plate
plate_thickness=0.5;

// diameter of the overall rotor (nothing will go outside this)
outside_dia=126;

// how large the intake hole is in the top
intake_dia=52.25;

// how many vanes there are
num_vanes=6;

// how thick the vanes are (0.5 is pushing our luck!)
vane_thickness=0.5;

// how large the shaft hole in the middle is.
shaft_dia=7.86;

// location where the vanes start (if <0 then use intake_dia)
vane_start_dia=-1;

// direction of the vanes
direction=0; // [0:counter clockwise,1:clockwise]

// gap controlling how tightly the vanes fit 0.2-0.3 is about right (higher number=looser)
vane_fit=0.25;

// resolution 1=low(aka 1x) 2=mid 3=high
resfactor=2;

/* [hidden] */

// make $fn more automatic
function myfn(r)=max(3*r,12)*resfactor;
module cyl(r=undef,h=undef,r1=undef,r2=undef){cylinder(r=r,h=h,r1=r1,r2=r2,$fn=myfn(r!=undef?r:max(r1,r2)));}
module circ(r=undef){circle(r=r,$fn=myfn(r));}
module ball(r=undef){sphere(r=r,$fn=myfn(r));}

function degrees2radians(a)=a*PI/180.0;
function radians2degrees(a)=a*180.0/PI;

// this comes from wikipedia
/*function involutePoint(r,t,a,cw)=[
	r*(cos(t)+(t-a)*sin(t))/360,
	r*(sin(t)+(cw==true?(t-a):-(t-a))*cos(t))/360
	];*/
// this comes from wolfram (kind of a more reliable source)
function involutePoint(r,t,a,cw)=[
	r*(cos(t)+degrees2radians(t)*sin(t)),
	r*(sin(t)-degrees2radians(t)*cos(t))*(cw==true?1:-1)
	];
function involuteAngleToRadius(angle)=cos(angle)+degrees2radians(angle)*sin(angle);
function involuteRadiusToAngle(radius,a)=radians2degrees(radius/a);

module involuteOf(r,a=0,degrees=360,clockwise=true,startDegrees=0){
	// sweeps the (convex) children objects along an involute
	inc=5;
	for(t=[startDegrees:inc:degrees-inc]) hull(){
		translate(involutePoint(r,t,a,clockwise)) children();
		translate(involutePoint(r,t+inc,a,clockwise)) children();
	}
}

module involuteLine(r,thick=1,a=0,degrees=360,clockwise=true,startDegrees=0){
	/*
	unlike Archimedes spirals, the levels of consecutive involute lines are parallel
	*/
	involuteOf(r,a,degrees,clockwise,startDegrees) circle(r=thick/2,$fn=6);
}

module involutePlate(r,thick=0.01,a=0,degrees=360,clockwise=true,startDegrees=0){
	inc=5;
	for(t=[startDegrees:inc:degrees-inc]) hull(){
		translate(involutePoint(r,t,a,clockwise)) circle(r=thick/2,$fn=6);
		translate(involutePoint(r,t+inc,a,clockwise)) circle(r=thick/2,$fn=6);
		circle(r=thick/2,$fn=6);
	}
}

function archimedesRadius(radians,a,b,c=1)=a+b*pow(radians,1/c);

module archimedesLine(a,b,c=1,thick=1,degrees=360,clockwise=true){
	/*
	a = inital offset
	b = spiral increase
	c = a cubic factor.  usually leave at 1.0, but there are related curves you can make by changing this
	*/
	inc=15;
	for(angle=[0:inc:degrees-inc]) hull(){
		rotate([0,0,angle]) translate([archimedesRadius(angle/(2*PI),a,b,c),0,0]) circle(r=thick/2,$fn=6);
		rotate([0,0,angle+inc]) translate([archimedesRadius((angle+inc)/(2*PI),a,b,c),0,0]) circle(r=thick/2,$fn=6);
	}
}

module archimedesPlate(a,b,c=1,degrees=360,clockwise=true){
	/*
	draw a flat plane representing an Archimedes curve

	a = initial offset
	b = spiral increase
	c = a cubic factor.  usually leave at 1.0, but there are related curves you can make by changing this
	*/
	thick=0.1;
	inc=15;
	for(angle=[0:inc:degrees-inc]) hull(){
		rotate([0,0,angle]) translate([archimedesRadius(angle/(2*PI),a,b,c),0,0]) circle(r=thick/2,$fn=6);
		rotate([0,0,angle+inc]) translate([archimedesRadius((angle+inc)/(2*PI),a,b,c),0,0]) circle(r=thick/2,$fn=6);
		//circle(r=thick/2,$fn=6); // for some baffling reason, this line crashes
	}
}

module vane(start_r,end_r,thickness=1,clockwise=true){
	a=100;
	startDegrees=involuteRadiusToAngle(start_r,a);
	endDegrees=involuteRadiusToAngle(end_r,a)+90;
	//echo(start_r,end_r,startDegrees,endDegrees);
	involuteLine(start_r,thick=thickness,a=a,degrees=endDegrees,clockwise=clockwise,startDegrees=startDegrees);
}

module impeller_fillet(fillet,num_vanes,vane_start_dia,outside_dia,vane_thickness,clockwise=true){
	a=100;
	start_r=vane_start_dia/2;
	end_r=outside_dia/2;
	startDegrees=involuteRadiusToAngle(start_r,a);
	endDegrees=involuteRadiusToAngle(end_r,a)+90;
	for(angle=[0:360/num_vanes:359.99]){
		rotate([0,0,angle]) involuteOf(start_r,a,endDegrees,clockwise,startDegrees) cylinder(r1=fillet+vane_thickness/2,r2=vane_thickness/2,h=fillet,$fn=6);
	}
}

module impellerBottom(outside_dia,intake_dia,total_height=10,num_vanes=6,plate_thickness=1,vane_thickness=0.5,shaft_dia=5,vane_start_dia=-1,fillet=2,clockwise=true){
	vane_start_dia=vane_start_dia>=0?vane_start_dia:intake_dia;
	intersection(){
		union(){
			linear_extrude(total_height) for(angle=[0:360/num_vanes:359.99]){
				rotate([0,0,angle]) vane(vane_start_dia/2,outside_dia/2,vane_thickness,clockwise);
			}
			// impeller_fillet
			translate([0,0,plate_thickness]) impeller_fillet(fillet,num_vanes,vane_start_dia,outside_dia,vane_thickness,clockwise);
			// bottom plate
			linear_extrude(plate_thickness) difference(){
				circ(r=outside_dia/2);
				circ(r=shaft_dia/2);
			}
		}
		// constrain to this boundary
		cyl(r=outside_dia/2,h=total_height-plate_thickness);
	}

}

module impellerTop(outside_dia,intake_dia,total_height=10,num_vanes=6,plate_thickness=1,vane_thickness=0.5,shaft_dia=5,vane_start_dia=-1,fillet=2,clockwise=true,vane_fit=0.2){
	vane_start_dia=vane_start_dia>=0?vane_start_dia:intake_dia;
	intersection(){
		union(){
			// plate
			cyl(r=outside_dia/2,h=plate_thickness);
			// fillet with a place for the vanes
			translate([0,0,plate_thickness]) difference() {
				impeller_fillet(fillet,num_vanes,vane_start_dia,outside_dia,vane_thickness,clockwise==false);
				linear_extrude(total_height) for(angle=[0:360/num_vanes:359.99]){
					rotate([0,0,angle]) vane(vane_start_dia/2,outside_dia/2,vane_thickness+vane_fit*2,clockwise==false);
				}
			}
		}
		// constrain to this boundary
		linear_extrude(total_height) difference(){
			circ(r=outside_dia/2);
			circ(r=intake_dia/2);
		}
	}
}

clockwise=direction==1;
if(show==0){
	impellerBottom(outside_dia,intake_dia,total_height,num_vanes,plate_thickness,vane_thickness,shaft_dia,vane_start_dia,clockwise);
	translate([0,0,total_height-plate_thickness]) rotate([180,0,0]) impellerTop(outside_dia,intake_dia,total_height,num_vanes,plate_thickness,vane_thickness,shaft_dia,vane_start_dia,clockwise,vane_fit);
}else if(show==1){
	impellerTop(outside_dia,intake_dia,total_height,num_vanes,plate_thickness,vane_thickness,shaft_dia,vane_start_dia,clockwise,vane_fit);
}else if(show==2){
	impellerBottom(outside_dia,intake_dia,total_height,num_vanes,plate_thickness,vane_thickness,shaft_dia,vane_start_dia,clockwise);
}
```