[User]
I'm looking for a parametric tie hanger design that I can customize in OpenSCAD. I want to be able to easily adjust things like the hook size, how many tie rows there are, the width of the tie sections, and whether it's a double-sided hanger or not.

Here are the specific parameters I'd like to control:

*   **Thickness:** The overall thickness of the hanger part in the Z dimension. I want to be able to set this to make the hanger sturdier.
*   **Wall Thickness:** The thickness of the walls in the X and Y dimensions, also for sturdiness.
*   **Hook Size:** The inner diameter of the hook, which I'll need to make slightly larger than the rod I'll be hanging it on.
*   **Hook Extra Space:**  How much extra space to add to the hook opening. I want to make it a bit larger or smaller than the hook size.
*   **Tie Count:** The number of tie rows/sections on the hanger.
*   **Double Sided:** A boolean flag to make the hanger double-sided or single-sided.
*   **Tie Section Width:** The width of the flat area of each tie section.
*   **Tie Section Hook Height:** The height of the little hook at the end of each tie section.
*   **Tie Section Gap Height:**  The height of the gap above the tie section hook.
*   **Tie Section Shift:** Only for double-sided hangers, this shifts each layer of tie sections outwards, to stagger them.

I imagine the design would have a main hook at the top, a horizontal bar connecting to the tie section supports, then the tie sections themselves with their little hooks. The design should use the hull() function to smoothly connect the cylindrical parts. I also need to ensure there is a ball at the end of the hook for smooth hanging.

Could you generate the OpenSCAD code to achieve this? I would prefer the code to be in a single file for simplicity. I don't need any libraries; please use built-in OpenSCAD functions.

[Assistant]
Tie_Hanger_v1.0.scad:
```

/* [General] */

// Thickness of the part in the Z dimension. Increase to make a sturdier part.
thickness=4; // [2:10]

// Thickness of the walls in the X and Y dimensions. Increase to make a sturdier part.
wall_thickness=5; // [3:8]

/* [Hook] */

// Inner diameter of the hook. Recommended to set this at least 0.5mm larger than the target rod.
hook_size=35.5; // [2:0.5:100]

// How much extra space to put in the hook opening. If set to 0, the opening will be exactly the same as Hook Size. Positive values will result in a larger opening, negative values will result in a smaller opening. Values lower than -(Hook Size / 2) would violate the geometry of the hook, so will automatically be changed to the lowest valid value.
hook_extra_space=0; // [-50:50]

/* [Tie Sections] */

// The number of tie rows on the hanger.
tie_count=4; // [1:20]

// Whether or not the hanger is double-sided.
double_sided=true; // [true,false]

// The width of each tie section flat area.
tie_section_width=40; // [20:60]

// The height of the hook at the end of each tie section.
tie_section_hook_height=2.5; // [1:0.5:5]

// The height of the gap above the tie section hook. Too small values will make it difficult to use.
tie_section_gap_height=5; // [1:10]

// Only works when Double Sided = true. Shift each layer of the tie section outward by this amount so that the ties are staggered.
tie_section_shift=0; // [0:50]

//Calculated
top_bar_y=wall_thickness/2;
each_bar_y_multiplier=-(wall_thickness+tie_section_hook_height+tie_section_gap_height);
tie_section_left_x=double_sided?(-(tie_section_width+wall_thickness*2)):-(tie_section_width+wall_thickness*2)/2;
tie_section_right_x=double_sided?(tie_section_width+wall_thickness*2):(tie_section_width+wall_thickness*2)/2;
real_hook_extra_space=(hook_extra_space<-(hook_size/2))?-(hook_size/2):hook_extra_space;

union() {
    // Main hook
    union() {

        // Hook surround
        difference() {
            // Outside
            hull() {
                // Bottom of hook should be at 0,0
                translate([0,hook_size/2+wall_thickness,0]) cylinder(d=hook_size+wall_thickness*2,h=thickness,$fn=40);
                // Top of the hook should be above
                translate([0,hook_size/2+wall_thickness+hook_size/2+real_hook_extra_space,0]) cylinder(d=hook_size+wall_thickness*2,h=thickness,$fn=40);
            }
            // Inside (subtracted)
            hull() {
                translate([0,hook_size/2+wall_thickness,0]) cylinder(d=hook_size,h=thickness*3,center=true,$fn=40);
                translate([0,hook_size/2+wall_thickness+hook_size/2+real_hook_extra_space,0]) cylinder(d=hook_size,h=thickness*3,center=true,$fn=40);
            }
            // Opening (subtracted)
            translate([-(hook_size/2+wall_thickness+1),0,-1]) cube([hook_size/2+wall_thickness+1,hook_size+wall_thickness*1+real_hook_extra_space,thickness*3]);
        }

        // Ball on end of hook
        translate([-(hook_size+wall_thickness)/2,(hook_size+wall_thickness+real_hook_extra_space),0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
    }

    // Straight across top bar
    translate([0,top_bar_y,0]) {
        hull() {
            translate([tie_section_left_x,0,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
            translate([tie_section_right_x,0,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
        }
    }

    middle_support_x=double_sided?0:(tie_section_width+wall_thickness*2)/2;
    middle_support_left_x=double_sided?middle_support_x-tie_count*tie_section_shift:middle_support_x;
    middle_support_right_x=double_sided?middle_support_x+tie_count*tie_section_shift:middle_support_x;
    // Middle support 1 (left)
    hull() {
        translate([middle_support_x,top_bar_y,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
        translate([middle_support_left_x,top_bar_y+tie_count*each_bar_y_multiplier,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
    }
    // Middle support 2 (right)
    hull() {
        translate([middle_support_x,top_bar_y,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
        translate([middle_support_right_x,top_bar_y+tie_count*each_bar_y_multiplier,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
    }

    // Each tie section
    for (i = [1:tie_count]) {
        this_section_left_x=double_sided?tie_section_left_x-(i*tie_section_shift):tie_section_left_x;
        this_section_right_x=double_sided?tie_section_right_x+(i*tie_section_shift):tie_section_right_x;
        translate([0,top_bar_y + (i*each_bar_y_multiplier),0]) {
            // Cross bar for tie section
            hull() {
                translate([this_section_left_x,0,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
                translate([this_section_right_x,0,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
            }
            // Left hook
            hull() {
                translate([this_section_left_x,0,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
                translate([this_section_left_x,tie_section_hook_height,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
            }
            // Right hook
            hull() {
                translate([this_section_right_x,0,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
                translate([this_section_right_x,tie_section_hook_height,0]) cylinder(d=wall_thickness,h=thickness,$fn=40);
            }
        }
    }
}
```


[User]
Hey, I'm looking to get a parametric bottle opener designed in OpenSCAD. I've got an example file I've been playing with, but I want to start fresh and get something a bit more robust. The basic idea is a bottle opener that grips around the lid, with some teeth to help with the opening.

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

**General:**

*   **Height:** The overall height of the opener.
*   **Grip Hole:**
    *   **Diameter:** The diameter of the main circular opening for gripping the lid.
    *   **Border:** The thickness of the material around the grip hole.
*   **Teeth:**
    *   **Number:** The number of teeth around the opening.
    *   **Height:** The height of each tooth.
    *   **Width:** The width of each tooth.
*   **Handle:**
    *   **Width:** The width of the handle.
    *   **Length:** The length of the handle.
    *   **Border:** The thickness of the material around the handle.

I'd also like to have a feature that allows for printing a test piece that is only the circular grip part, to make testing different sizes easier. I was using a boolean for this, but it was clunky, so if you could do that more elegantly, it would be nice.  I don't need anything special about the test feature other than it should just be the grip section, a low height, and easily toggled on/off.

I would also like to be able to adjust the quality with the `$fa` and `$fs` variables.

I'm looking for a single, well-organized SCAD file for this. Could you generate that for me?

[Assistant]
bottle_opener.scad:
```
/* [General] */

height = 15; // [1:50]

/* [Grip hole] */

diameter = 37.7; // [5:0.1:100]
border = 2.5; // [1:0.1:20]

/* [Teeth] */

teeth_number = 30; // [1:500]
teeth_height = 2; // [0.1:0.1:50]
teeth_width = 2; // [0.1:0.1:50]

/* [Handle] */

handle_width = 25; // [5:50]
handle_length = 90; // [50: 500]
handle_border = 2; // [1:0.1:25]

/* [Test] */

test = "false"; // [true, false]

/* [Hidden] */

subtract_safe = 1;
$fa = 0.1;
$fs = 1;

module oval(length, width, height) {
     translate([length / 2 - width / 2, 0, 0])
	  cylinder(r = width / 2, h = height);
     translate([-length / 2 + width / 2, 0, 0])
	  cylinder(r = width / 2, h = height);
     translate([0, 0, height / 2])
	  cube([length - width, width, height], center = true);
}

module solid() {
     union() {
	  translate([handle_length + diameter / 2 + border, 0, 0])
	       cylinder(d = diameter + border * 2, h = height);

	  if (test == "false") {
	       hull() {
		    translate([handle_length + diameter / 2 + border, 0, 0])
			 cylinder(d = diameter + border * 2, h = height);
		    translate([handle_length - handle_border, 0, 0])
			 cylinder(d = handle_width + handle_border, h = height);
	       }

	       hull() {
		    translate([handle_length - handle_border, 0, 0])
			 cylinder(d = handle_width + handle_border, h = height);
		    translate([handle_length - handle_width, 0, 0])
			 cylinder(d = handle_width / 2 + handle_border * 3, h = height);
	       }

	       hull() {
		    translate([handle_length - handle_width, 0, 0])
			 cylinder(d = handle_width / 2 + handle_border * 3, h = height);
		    translate([handle_length / 2, 0, 0])
			 cylinder(d = handle_width / 2 + handle_border * 2, h = height);
	       }

	       hull() {
		    translate([handle_length - handle_width, 0, 0])
			 cylinder(d = handle_width / 2 + handle_border * 2, h = height);
		    translate([handle_width , 0, 0])
			 cylinder(d = handle_width / 2 + handle_border * 3, h = height);
	       }

	       hull() {
		    translate([handle_length / 2, 0, 0])
			 cylinder(d = handle_width / 2 + handle_border * 2, h = height);
		    translate([handle_width / 2, 0, 0])
			 cylinder(r = handle_width / 2, h = height);
	       }

	       translate([handle_width / 2, 0, 0])
		    cylinder(r = handle_width / 2, h = height);
	  }
     }
}

module holes() {
     union() {
	  translate([handle_length + diameter / 2 + border, 0, -subtract_safe])
	       cylinder(d = diameter, h = height + subtract_safe * 2);

	  if (test == "false") {
	       translate([handle_length / 2 + handle_width / 2, 0, -subtract_safe])
		    oval(handle_length - handle_width - handle_border * 2, handle_width * 5 / 12, height + subtract_safe * 2);

	       translate([handle_width / 2, 0, -subtract_safe])
		    cylinder(r = handle_width / 2 - handle_border, h = height + subtract_safe * 2);
	  }
     }
}

module teeth() {
     translate([handle_length + diameter / 2 + border, 0, 0])
     union() {
	  for(i = [0:teeth_number]) {
	       rotate([0, 0, i * 360 / teeth_number])
		    translate([diameter / 2, 0, 0])
		    linear_extrude(height = height, convexity = 10, twist = 0)
		    polygon(points=[[-teeth_height, 0],[0, -teeth_width / 2],[0, teeth_width / 2]], paths=[[0, 1, 2]]);
	  }
     }
}

union() {
     difference() {
	  solid();
	  holes();
     }
     teeth();
}

```


[User]
Hey, I'm looking to generate some servo horns, and I'd like to be able to control several parameters. I've seen a few different ways to do it, but I'd like something that's pretty flexible.

Here's what I'm thinking:

I need a main servo horn body with a central spline hole that interfaces with standard servo motors, and then a number of arms extending from it. I'd like the following parameters:

*   **HoleSize:** The diameter of the holes in the arms for connecting to other parts (e.g. linkages). This should be adjustable from 1.0 to 6.0 mm in 0.1mm increments.
*   **ArmNumber:** The number of arms extending from the servo horn. This needs to be an integer between 1 and 8 (inclusive).
*   **MinimumRadius:** The distance from the center of the servo shaft to the center of the *first* hole on an arm. This should be adjustable between 6.0 and 75.0 mm in 0.1mm increments.
*   **MaximumRadius:** The distance from the center of the servo shaft to the center of the *last* hole on an arm. This should be adjustable between 6.1 and 80.0 mm in 0.1mm increments.
*   **HolesPerArm:** The number of holes on each arm. This should be an integer between 1 and 10 (inclusive).
*   **Thickness:** The overall thickness of the servo horn. This needs to be adjustable from 2.0 to 8.0 mm in 0.1mm increments.
*   **TeethCount:** The number of teeth on the servo shaft spline. I need to be able to choose between 24 and 25 teeth for different servos.
*   **SetScrewSize:** The diameter of a set screw hole for added stability. If this is set to zero, it should be disabled.  Adjustable from 0.0 to 4.0 in 0.2 mm increments

I'd really like for the arms to be rectangular, with rounded ends, and the inner edge where arms meet the central body should be filleted. The central spline hole needs to be a fairly precise fit, so using a polygon with a number of points approximating the star shape is important. I've seen some examples of how to do that. I'd also like a recessed circle around the shaft hole on the top of the horn so that servo head screws don't interfere with the horn. This should only be added if the horn's thickness is greater than 6mm, and have a depth of approximately 1.5mm.

Could you generate the OpenSCAD code for this? I don't have any specific libraries I need to use beyond the built in ones, but if you have a good reason for using one I'm okay with that.

[Assistant]
ServoHorn.scad:
```
/*----------------------------------------------------------------------------*/
/*  Standard Servo Motor Horn
*/
/*  Eric Lindholm
*/
/*  7 September 2016                                                                  */
/*  License: Creative Commons - Attribution - Non-Commercial                  */
/*----------------------------------------------------------------------------*/

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

// The size of the screw hole requested. Not the size of the servo shaft (mm).
HoleSize=2.1; // [1.0:0.1:6]
// How many arms (horns) there are.
ArmNumber=3; // [1:1:8]
// The distance from the first hole to the center of the shaft (mm). If you have less than three arms, recommend somewhere over 9 mm.
MinimumRadius=8; // [6.0:0.1:75.0]
// The distance from the last hole to the center of the shaft (mm).
MaximumRadius=18; // [6.1:0.1:80.0]
// The number of holes PER ARM.
HolesPerArm=3; // [1:1:10]
// The total thickness of the horn (mm).
Thickness=5; // [2.0:0.1:8.0]

// Edited April 2020: So, there are different splines for servo motors (the output shafts). They have different numbers of teeth and are slightly different sizes. The most common ones are 24 and 25 teeth, although others exist.
// Good luck counting them. It's better to just look up what kind your servo has online.
// Number of teeth on the spline
TeethCount=24;   // [24,25]
// 3D printers can't make the spline very well. So, it seems like a good idea to include hole for a set screw to really fasten this thing on. Set to zero to disable.
SetScrewSize=2.2;   // [0.0:0.2:4.0]

/* [Hidden] */
// This number controls the number of segments in a circle. I like using 36, but this can totally be changed if you like nicer arcs.
$fn=72;
// Degrees between arms
dgamma=360/ArmNumber;
//echo(dgamma);
// arm width multiplier
arm_w_m=3.5;
// The central cylinder outer radius
R_center=MinimumRadius-HoleSize*0.55;

difference() {
	// Positive shapes
	union() {
		// central cylinder
        cylinder(r=ArmNumber<3?R_center:3.2,h=Thickness);
		// The arms
		if(ArmNumber==1) {
			armsolo();
		}
		if(ArmNumber>1) {
			for(n=[0:ArmNumber]) {
				arm(dgamma*n);
			}
		}
        // Note: if ArmNumber is less than 1, you're not actually making a servo horn.
	}
	// Negative shapes
	// For the shaft hole, the process can be speeded up immensely
	// by using a cylinder instead of the module I created for the star
	// But if you have a 3D printer that can handle the detail, go for it
	union() {
		// screwhole
		cylinder(r=1.4,h=Thickness*3,center=true);
        // indent for screw head, if necessary.
        if(Thickness>6) {
            translate([0,0,-1])
                cylinder(r=R_center-1.5, h=Thickness-5, center=false);
        }
		// shaft hole
		translate([0,0,Thickness>6?Thickness-4.5:1.5]) {
            if(TeethCount==25)
                servoHole25T();
            if(TeethCount==24)
                servoHole24T();
        }
			//cylinder(r1=3.0,r2=3.1,h=Thickness+1);
        // Set screw
        if(SetScrewSize>0) {
            rotate(-dgamma/2, [0,0,1])  // haha it doesn't matter that this is negative.
                translate([0,0,Thickness-2.2])
                    rotate(-90,[1,0,0])
                        cylinder(r=SetScrewSize/2, h=R_center*3, center=false);
        }
	}
}

// The arm module
module arm(gamma) {
	// The offset for the rectangle
	centerArm=(4.5+MaximumRadius)/2;
	// The length of the rectangle
	armLength=(MaximumRadius-4.5)+HoleSize;
    armW=HoleSize*arm_w_m;
	//holeDelta=1;
	//if(HolesPerArm!=0)
	// The distance between holes on the arm
	holeDelta=(HolesPerArm>1) ? (MaximumRadius-MinimumRadius)/(HolesPerArm-1) : 0;
	// The radius of the smoothed corner between arms
	cornerR=3.0*R_center/ArmNumber;
    //echo(cornerR);
	// We rotate the arm by the specified angle
	rotate(gamma,[0,0,1]) {
		difference() {
			// Positive shapes
			union() {
				// Primary rectangle
				translate([0,centerArm-HoleSize/2,Thickness/2])
					cube([armW,armLength+HoleSize,Thickness],center=true);
				// Rounded end of arm
				translate([0,MaximumRadius+HoleSize/2,Thickness/2])
					cylinder(r=armW/2,h=Thickness,center=true);
				// Section we remove from to get the smoothed corner between arms
				translate([armW/2,(armW/2)/tan(dgamma/2),Thickness/2])
					linear_extrude(height=Thickness,convexity=10,twist=0,center=true)
						polygon(points=[[0,0],[0,cornerR/tan(dgamma/2)],[cornerR*sin(dgamma)/tan(dgamma/2),cornerR*cos(dgamma)/tan(dgamma/2)]]);
			}

			// Negative shapes
			union() {
				// Do all the holes from Min to Max
				for(p=[1:HolesPerArm]) {
					translate([0,MaximumRadius-(p-1)*holeDelta,Thickness/2])
						cylinder(r=HoleSize/2,h=Thickness+2,center=true);
				}
				// Rounding the gap
				translate([(armW/2) + cornerR,(armW/2)/tan(dgamma/2) + cornerR/tan(dgamma/2),Thickness/2])
					cylinder(r=cornerR,h=Thickness+2,center=true);
                // the rounded corners don't quite work, except for armnumber=4.
			}
		}
	}
}

module armsolo() {
	// The offset for the rectangle
	centerArm=(4.5+MaximumRadius)/2;
	// The length of the rectangle
	armLength=(MaximumRadius-4.5)+HoleSize;
	// The distance between holes on the arm
	holeDelta= (HolesPerArm>1) ? (MaximumRadius-MinimumRadius)/(HolesPerArm-1) : 0;
	difference() {
		// Positive shapes
		union() {
			// Primary rectangle
			translate([0,centerArm-HoleSize/2,Thickness/2])
				cube([HoleSize*3,armLength+HoleSize,Thickness],center=true);
			// Rounded end of arm
			translate([0,MaximumRadius+HoleSize/2,Thickness/2])				cylinder(r=HoleSize*1.5,h=Thickness,center=true);
		}

		// Negative shapes
		union() {
			// Do all the holes from Min to Max
			for(p=[1:HolesPerArm]) {
				translate([0,MaximumRadius-(p-1)*holeDelta,Thickness/2])
					cylinder(r=HoleSize*0.525,h=Thickness+2,center=true);
			}
		}
	}
}

// The servo shaft module
// On my printer this was too tight for the shaft. I think for a better calibrated
//  printer, it would work great.
module servoHole25T() {
	// Beta is the angle for one slice
	beta=360/25;
	// The external radius is the outer edge of the star
	exR=2.95;
	// The depth of the shaft hole
	depth=5;
	// Factor for how vertical the sides are
	inTilt=0.95;
	// Difference between inner and outer radius
	inR=0.20;
	// This 'shaft hole' is really a 25-pointed star that's narrower at the top.
	// The most reliable way I could think to do this was using a wedge polyhedron,
	//  copied 25 times.
	union() {
		for(r=[0:25]) {
			rotate(r*beta,[0,0,1])
				polyhedron(points=[
					[0,0,0],	//A
					[0,0,depth],	//B
					[exR*cos(beta/2),-1.01*exR*sin(beta/2),depth], //C
					[inTilt*exR*cos(beta/2),-1.01*inTilt*exR*sin(beta/2),0],	//D
					[inTilt*(exR-inR),0,0],	//E
					[exR-inR,0,depth],	//F
					[inTilt*exR*cos(beta/2),inTilt*exR*sin(beta/2),0],	//G
					[exR*cos(beta/2),exR*sin(beta/2),depth]]	//H
				,faces=[
					[0,3,2,1],	//ADCB
					[3,4,5,2],	//DEFC
					[4,6,7,5],	//EGHF
					[6,0,1,7],	//GABH
					[0,6,4,3],	//AGED
					[1,2,5,7]],	//BCFH
					convexity=10);	// Actually only has convexity of 4.
				/*
				difference() {
					translate([((exR*cos(beta/2)-.43)/2),0,])
						cube([exR*cos(beta/2)+1,2.2*exR*sin(beta/2),depth],center=true);
					linear_extrude(height=depth+1,convexity=10,twist=0,center=true)
						polygon(points=[[1.1*exR*cos(beta/2),1.1*exR*sin(beta/2)],[1.1*exR*cos(beta/2),-1.1*exR*sin(beta/2)],[exR-0.15,0]]);
				}
				*/
		}
	}
}

// Servo Shaft module for larger servos.
module servoHole24T() {
    // Mostly copied from the 25 tooth version.
    // Beta is the angle for one slice
	beta=360/24;
	// The external radius is the outer edge of the star
	exR=2.875;
	// The depth of the shaft hole
	depth=5;
	// Factor for how vertical the sides are
	inTilt=0.95;
	// Difference between inner and outer radius (mm)
	inR=0.20;
	// This 'shaft hole' is really a 24-pointed star that's narrower at the top.
	// The most reliable way I could think to do this was using a wedge polyhedron,
	//  copied 24 times.
	union() {
		for(r=[0:24]) {
			rotate(r*beta,[0,0,1])
				polyhedron(points=[
					[0,0,0],	//A
					[0,0,depth],	//B
					[exR*cos(beta/2),-1.01*exR*sin(beta/2),depth], //C
					[inTilt*exR*cos(beta/2),-1.01*inTilt*exR*sin(beta/2),0],	//D
					[inTilt*(exR-inR),0,0],	//E
					[exR-inR,0,depth],	//F
					[inTilt*exR*cos(beta/2),inTilt*exR*sin(beta/2),0],	//G
					[exR*cos(beta/2),exR*sin(beta/2),depth]]	//H
				,faces=[
					[0,3,2,1],	//ADCB
					[3,4,5,2],	//DEFC
					[4,6,7,5],	//EGHF
					[6,0,1,7],	//GABH
					[0,6,4,3],	//AGED
					[1,2,5,7]],	//BCFH
					convexity=10);	// Actually only has convexity of 4.
        }
    }
}
```


[User]
Hey, I'm looking for an OpenSCAD design for a parametric funnel, and I think I have a good idea of how it should work.

Basically, I want a funnel where I can control the following:

*   **Overall Height:**  The total height of the funnel from top to bottom.
*   **Top Inside Diameter:** The diameter of the opening at the top of the funnel's inner surface.
*  **Top Ledge Width:**  The width of a small ledge/overhang at the top of the funnel, kind of like a rim.
*   **Tube Height:** The height of the cylindrical tube at the bottom of the funnel.
*   **Tube Outside Diameter:** The outside diameter of the cylindrical tube at the bottom.
*   **Tube Bottom Cut Angle:** The angle at which the bottom of the tube is cut, allowing it to be slanted.
*   **Wall Thickness:**  The thickness of the walls of the funnel.

I envision the funnel being made up of a frustum for the main funnel shape and a cylinder for the bottom tube. I also want that top ledge/rim to extend outwards from the frustum. I'd like to be able to define all of those parameters.

So, the funnel should have a top opening defined by the *Top Inside Diameter* which transitions down to the *Tube Outside Diameter* over the *Total Height*.  The base of that transition area should have the cylinder of the *Tube Height* attached. I'd like to have a rim or ledge which is controlled by the *Top Ledge Width* and should overhang the funnel. Finally, the very bottom of the tube can have a cut at the *Tube Bottom Cut Angle*. The *Wall Thickness* should be applied consistently throughout.

I'm not very familiar with OpenSCAD myself, so I'm hoping you can generate a .scad file (or files) that has these parameters fully configurable. I would also like to use the BOSL2 library for any relevant functions.

[Assistant]
parametric_funnel.scad:
```
// Code written by "Shii" Sep 2016

// Total Height
Height=38;

// Inner Top Diameter
Top_Inside_Diameter=38;

// Top Ledge Width
Ledge=3;

// Tube Height
Tube_Height=15;

// Tube Outside Diameter
Tube_Outside_Diameter=6;

// Tube Bottom Cut Angle
Tube_Cut_Angle=30;

// Wall Thickness
Wall_Thickness=0.8;

/* [Hidden] */
$fn = 250;
Top_Height = Height - Tube_Height;
Top_Outside_Diameter = Top_Inside_Diameter + Wall_Thickness*2;
Ledge_Multiplicator = 2.6;

rotate([180,0,0]) translate([0,0,-Height]) {

  // Funnel top ring
  translate ([0,0,Height-Wall_Thickness*2]) difference(){
    cylinder(d = Top_Inside_Diameter+Ledge*2, h = Wall_Thickness*2);
    translate ([0,0,-0.1]) cylinder(d = Top_Inside_Diameter, h = Wall_Thickness*2+0.2);
  }

  // Funnel top ring lip
  translate ([(Top_Outside_Diameter+Ledge*3)/2,0,Height-Wall_Thickness*2]) difference(){
    union(){
      translate ([-Ledge*1.5-Wall_Thickness,-Ledge*Ledge_Multiplicator/2,0]) cube([Ledge*1.5+Wall_Thickness,Ledge*Ledge_Multiplicator,Wall_Thickness*2]);
      cylinder(d = Ledge*Ledge_Multiplicator, h = Wall_Thickness*2);
    }
    translate ([0,0,-0.1]) cylinder(d = Ledge*Ledge_Multiplicator-2*Ledge, h = (Wall_Thickness*2)+0.2);
  }

  // Funnel top
  translate ([0,0,Tube_Height]) difference(){
    union(){
      cylinder(d1 = Tube_Outside_Diameter, d2 = Top_Outside_Diameter, h = Top_Height-Wall_Thickness);
      translate ([0,Tube_Outside_Diameter/2,0]) rotate([-atan((Top_Outside_Diameter/2 - Tube_Outside_Diameter/2)/(Top_Height-Wall_Thickness)),0,0]) union(){
        cylinder(d = Wall_Thickness, h = Top_Height-Wall_Thickness);
        translate ([0,0,Top_Height-Wall_Thickness])sphere(d=Wall_Thickness);
      }
    }
    translate ([0,0,+0.1])cylinder(d1 = Tube_Outside_Diameter - Wall_Thickness*2, d2 = Top_Outside_Diameter - Wall_Thickness*2+0.2, h = Top_Height-Wall_Thickness);
    translate ([0,0,-0.1]) cylinder(d = Tube_Outside_Diameter - Wall_Thickness*2, h = 0.3);
  }

  // Funnel Tube
  difference(){
    union(){
      cylinder(d = Tube_Outside_Diameter, h = Tube_Height);
      translate ([0,Tube_Outside_Diameter/2,0])cylinder(d = Wall_Thickness, h = Tube_Height);
    }
    translate ([0,0,-0.1]) cylinder(d = Tube_Outside_Diameter - Wall_Thickness*2, h = Tube_Height+0.2);
    translate ([-Tube_Outside_Diameter/2,-Tube_Outside_Diameter/2,0]) rotate([Tube_Cut_Angle,0,0]) mirror([0,0,1]) cube([Tube_Outside_Diameter,Tube_Height*3,Tube_Outside_Diameter]);
  }
}

```


[User]
I'd like to design a customizable motorcycle license plate holder using OpenSCAD. I've got some specific dimensions and features in mind, and it needs to be parametric so I can easily adjust it later.

First, the basic shape should be a rectangular plate with rounded corners. The overall size of the plate should be adjustable, but I'm starting with a border distance of 184.15mm in the x-direction and 121mm in the y-direction. The corners need a radius of 12mm. The whole thing should have a thickness, which I'll call 'total_height' and will start at 3mm. The main body should have a wall thickness of 3mm.

There needs to be an open area inside this border, where the license plate will sit. The open area should have an adjustable offset from all sides. I'll start with a side border of 16.2mm, a top border of 19.5mm, and a bottom border of 25mm.

Then, I need four mounting holes for the license plate. They should be for 3.75mm radius screws, which I'll call 'hole_radius'. The centers of these holes should be located with the following offsets: hole_offset_x of 15.5mm, a hole_offset_top_y of 19.4mm, and a hole_offset_bottom_y of 24.5mm. They are to be arranged at the corners of a rectangle with hole center distance of 146mm in x and 70mm in y. Also, each of the screw holes should have a circular border, I'm thinking a radius of `hole_radius + wall_width`.

Finally, I need two lines of customizable text. One above the license plate area, and one below. I want to be able to change the text, font size, and vertical position for each line. There will also be a multiplier for extra space around each text character, which I want to be able to adjust. Also the text needs to be extruded to make it raised. The text should be centered horizontally in the open area. I will start with the following values:

*   Top text: "Customizable", font size 13, spacing of 1.1, text_height of 2
*   Bottom text: "3D Printing", font size 13, spacing of 1.1, text_height of 2

I think I'd like this organized into modules, such as a module for the screw holes, for the border, the open area, and for the two lines of text. I don't need any specific libraries - it all can be done from core openscad. Thanks!

[Assistant]
customizable_motorcycle_license_plate_holder.scad:
```
// Customizable section

top_text = "Customizable";
top_text_font_size = 13;

bottom_text = "3D Printing";
bottom_text_font_size = 13;

// Multiplier for extra space around each text character; slightly more space should prevent stringing and make text easier to paint. If you want to fit more text, go down to 1.0
spacing = 1.1; //[1.0:Original Size, 1.1:1.1x spacing, 1.2:1.2x spacing, 1.3:1.3x spacing]

// How high to raise up the text, in mm
text_height = 2;

/* [Hidden] */

hole_radius = 3.75;
hole_center_distance_x = 146;
hole_center_distance_y = 70;
hole_offset_x = 15.5 + hole_radius;
hole_offset_top_y = 19.4 + hole_radius;
hole_offset_bottom_y = 24.5 + hole_radius;

wall_width = 3;
total_height = 3;

border_distance_x = 184.15;
border_distance_y = 121;

corner_radius = 12;

side_border = 16.2;
top_border = 19.5;
bottom_border = 25;

module screw_hole() {
    cylinder(r=hole_radius, h=total_height);
}

module mounting_screw_holes() {
    translate([hole_offset_x, hole_offset_bottom_y, 0]) {
        screw_hole();
        translate([hole_center_distance_x,0,0]) { screw_hole(); }
        translate([hole_center_distance_x,hole_center_distance_y,0]) { screw_hole(); }
        translate([0, hole_center_distance_y, 0]) { screw_hole(); }
    }
}

module screw_hole_border() {
        square_part = 2 * wall_width + 2*hole_radius;
        translate([0,0,total_height/2]) {
            cylinder(r=hole_radius + wall_width, h=total_height, center = true);
        }

}

module mounting_screw_holes_border() {
    translate([hole_offset_x, hole_offset_bottom_y, 0]) {
        screw_hole_border();
        translate([hole_center_distance_x,0,0]) { screw_hole_border(); }
        translate([hole_center_distance_x,hole_center_distance_y,0]) { screw_hole_border(); }
        translate([0, hole_center_distance_y, 0]) { screw_hole_border(); }
    }
}

module border(border_distance_x, border_distance_y, wall_width, offset) {
    translate([offset,offset,0]) {
        // main body
        translate([corner_radius, 0, 0]) {
            cube([border_distance_x - 2*corner_radius, border_distance_y, wall_width]);
        }
        translate([0,corner_radius, 0]) {
            cube([border_distance_x, border_distance_y - 2*corner_radius, wall_width]);
        }

        // rounded corners
        translate([corner_radius, corner_radius, 0]) {
            cylinder(r=corner_radius, h=wall_width);
        }
        translate([border_distance_x - corner_radius, corner_radius, 0]) {
            cylinder(r=corner_radius, h=wall_width);
        }
        translate([border_distance_x - corner_radius, border_distance_y - corner_radius, 0]) {
            cylinder(r=corner_radius, h=wall_width);
        }
        translate([corner_radius, border_distance_y - corner_radius, 0]) {
            cylinder(r=corner_radius, h=wall_width);
        }
    }
}

module open_plate_area() {
        translate([side_border, bottom_border, 0]) {
            cube([border_distance_x - 2 * side_border, border_distance_y - top_border - bottom_border, total_height]);
        }
}

module top_text() {
        translate([border_distance_x / 2, border_distance_y - (top_border / 2), total_height]) {
                linear_extrude(text_height) {
                    text(top_text, halign = "center", valign = "center", size=top_text_font_size, spacing=spacing);
                }
        }
}

module bottom_text() {
        translate([border_distance_x / 2, bottom_border / 2, total_height]) {
                linear_extrude(text_height) {
                    text(bottom_text, halign = "center", valign = "center", size=bottom_text_font_size, spacing = spacing);
                }
        }
}

top_text();
bottom_text();

difference() {
    union() {
        difference() {
            // main body
            border(border_distance_x, border_distance_y, total_height, 0);
            open_plate_area();
        }
        difference() {
            mounting_screw_holes_border();
        }
    }
    // clear out room for screws
    mounting_screw_holes();

}

```


[User]
I need an OpenSCAD design for a fan adapter plate. I want to be able to mount a computer fan to a hole that it doesn't quite fit, and I'd like to be able to print a custom adapter for the task.

The design should be fully parametric, allowing the user to set the dimensions of the fan and the dimensions of the hole to which it is to be mounted. There are eight standard fan sizes I'd like to support: 40mm, 50mm, 60mm, 70mm, 80mm, 92mm, 120mm, and 140mm.

The design should also include an option to "save plastic" on larger adapters by removing material from the adapter plate where it isn't needed. I was thinking of a couple of levels of plastic saving, like a "Yes" option that removes some material from around the fan and a "Yes+Extra" option that removes even more.

Here's where it gets a little tricky: the screw holes should be rotated in some cases to prevent them from being too close together. You should be able to toggle this rotation on and off.

I don't want to include any external libraries in the design, except for those from the MCAD library. Could you make sure that everything is compatible with that library?

For the plate itself, it should be a rounded square with the fan hole in the middle. It should have screw slots for mounting both the fan and the plate. The screw slots should be oblong to allow for slight adjustments during mounting. If the "save plastic" option is enabled, material should be removed from around the fan hole, leaving just enough to connect the outer part of the plate to the inner part.

If the fan size and the aperture size are the same, the design should just output a 1x1x1 cube as a placeholder. This will let me know that no adapter is needed in that case.

Can you generate an OpenSCAD file or files that implement this design? Please make sure the code is well-commented so I can understand how it works.

[Assistant]
FanPlate4.scad:
```
// The size of the fan.
fan_size = 70; //[40:40mm, 50:50mm, 60:60mm, 70:70mm, 80:80mm, 92:92mm, 120:120mm, 140:140mm]

// The size of the hole you want to put it in.
aperture_size = 92; //[40:40mm, 50:50mm, 60:60mm, 70:70mm, 80:80mm, 92:92mm, 120:120mm, 140:140mm]

// Save plastic on larger adapters.
make_gaps = 1;//[0:No, 1:Yes, 2:Yes+Extra]

// Prevent screw holes from being too close together
rotate_inner_holes = 1;//[0:No, 1:Yes]

fanScrewDist = fanSize2ScrewDist(fan_size);
apertureScrewDist = fanSize2ScrewDist(aperture_size);
rad = min(fan_size, aperture_size);

if(fan_size != aperture_size)
if(fan_size == 40)
{
    if(aperture_size == 50)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 15);
    }
    else
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad);
    }
}
else if(fan_size == 50)
{
    if(aperture_size == 40)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 19);
    }
    else if(aperture_size == 60)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 13);
    }
    else
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad);
    }
}
else if(fan_size == 60)
{
    if(aperture_size == 50)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 16);
    }
    else if(aperture_size == 70)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 11);
    }
    else
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad);
    }
}
else if(fan_size == 70)
{
    if(aperture_size == 60)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 12);
    }
    else if(aperture_size == 80)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 8.5);
    }
    else
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad);
    }
}
else if(fan_size == 80)
{
    if(aperture_size == 70)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 10);
    }
    else if(aperture_size == 92)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 7);
    }
    else
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad);
    }
}
else if(fan_size == 92)
{
    if(aperture_size == 80)
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad, 8);
    }
    else
    {
        fanPlate(fanScrewDist, apertureScrewDist, rad);
    }
}
else if(fan_size == 120 || fan_size == 140)
{
    fanPlate(fanScrewDist, apertureScrewDist, rad);
}

if(fan_size == aperture_size)
    cube([1, 1, 1], true);

module fanPlate(innerLength, outerLength, diameter, rot = 0)
{
    rot = rot * rotate_inner_holes;
    sideLength = max(innerLength, outerLength) + 13;

    rotA = (innerLength > outerLength)?rot:0;
    rotB = (innerLength < outerLength)?rot:0;

    difference()
    {
        translate([0, 0, -2.5])
        linear_extrude(height=5)
        difference()
        {
            roundedSquare2D(sideLength, 6);
            circle(diameter/2, true, $fn=96);
            gaps2D(diameter/2, sideLength, max(rotA, rotB));
        }

        rotate(rotA)
        for(x=[1, -1])
        for(y=[1, -1])
        {
            translate([x*outerLength/2, y*outerLength/2, -3])
            rotate(x*y*45)
            linear_extrude(6)
            oblong2D(2.5, 1);
        }

        rotate(rotB)
        for(x=[1, -1])
        for(y=[1, -1])
        {
            translate([x*innerLength/2, y*innerLength/2, 0])
            rotate(x*y*45)
            screwSlot();
        }
    }
}

module roundedSquare2D(sideLen, radius)
{
    innerSideLen = sideLen - 2*radius;
    union()
    {
        square([innerSideLen, sideLen], true);
        square([sideLen, innerSideLen], true);
        for(x=[1, -1])
        for(y=[1, -1])
            translate([x*innerSideLen/2, y*innerSideLen/2, 0])
            circle(radius, true, $fn=96);
    }
}

module gaps2D(rad, len, rot)
{
    if(make_gaps > 0)
    if(rad+4 < (len-7)/1.8)
    difference()
    {
        square(len-7, true);
        union()
        {
            if(make_gaps == 1)
              circle(rad+3.5, true, $fn=96);
            else if(make_gaps == 2)
              circle(rad-0.1, true, $fn=96);

            rotate(45+rot/2)
            {
                //square([2*len, 13+rot*0.9], true);
                //square([13+rot*0.9, 2*len], true);
                w = 14 + rot*(2*3.141592*rad/360);
                square([2*len, w], true);
                square([w, 2*len], true);
            }
        }
    }
}

module screwSlot()
{
    translate([0, 0, 0.5])
    union()
    {
        translate([0, 0, -3.5])
        linear_extrude(height=4)
        oblong2D(2.8,2);

        translate([0, 0, 0])
        linear_extrude(height=3)
        oblong2D(4.1,2);
    }
}

module oblong2D(radius, length)
{
    union()
    {
        translate([-length/2, 0, 0])
            circle(radius, true, $fn=96);
        translate([length/2, 0, 0])
            circle(radius, true, $fn=96);
        square([length, radius*2], true);
    }
}

function fanSize2ScrewDist(fan_size)=
(fan_size ==  40)? 32.0:
(fan_size ==  50)? 40.0:
(fan_size ==  60)? 50.0:
(fan_size ==  70)? 61.5:
(fan_size ==  80)? 71.5:
(fan_size ==  92)? 82.5:
(fan_size == 120)?105.0:
(fan_size == 140)?125.0:0;

```


[User]
I'd like to design a parametric propeller or fan in OpenSCAD. I need to be able to adjust several things, like the number of blades, their length, the overall depth of the propeller, the thickness of the blades, and the diameter of the center hole for the shaft. Also, I want to have an option to include a flat side in the shaft hole for keying.

Specifically, I want these parameters:

*   `blades`: An integer controlling the number of blades, probably in the range of 1 to 75.
*   `length`: The length of each blade, in the range of 1 to 300.
*   `depth`: The overall depth or thickness of the propeller, with a range of 1 to 75.
*   `t`: The thickness of each blade, from 0.1 to 7.
*   `shaft`: The diameter of the center shaft hole, between 0 and 20.
*   `cut`: A boolean (or integer that is either 0 or 1) that controls if there is a flat side in the shaft hole.

The blades should be angled at 45 degrees, so they can be printed without supports. Make sure it's possible to make very shallow and deep propellers. The center hole should also have its size adjusted parametrically. It should cut through the whole thing, of course.

I think that covers it. Please let me know if anything is unclear.

[Assistant]
Propeller.scad:
```
// preview[view:north east, tilt:top diagonal]

// How many blades?
blades = 10; // [1:1:75]

// Length of blades
length = 50; // [1:1:300]

// Depth of propeller
depth = 12; // [1:1:75]

// How thick shall the blades be?
t = 2; // [0.1:0.1:7]

// Center hole for the shaft
shaft = 3; // [0:0.1:20]

// Do you want one flat side in the shaft hole?
cut = 1; // [1:Yes,0:No]

/* [Hidden] */
angle = 45;

difference() {
    ///////////////////////
    // Draw these things //
    ///////////////////////
    union(){
        for (rotation =[0:360/blades:360]) {
            rotate([0,0,rotation]) {
                translate([0,8,-5]) {
                    rotate([angle,0,0]) {
                        // Draw blades
                        cube([length, t, depth/sin(angle)+10],false);
                    } // End rotate
                } // End translate
            } // End rotate
        } // End for spaceW

    if (depth>20) {
        // Draw center cylinder for deep propellers
        cylinder(h=depth, r1=depth/4, r2=depth/3, center=false, $fn=40);
        } else {
        // Draw center cylinder for shallow propellers
        cylinder(h=depth, r1=9, r2=depth/1.3, center=false, $fn=30);
        } // End else
    } // End union

    ///////////////////////////
    // Cut away these things //
    ///////////////////////////

    // Draw shaft hole
    translate([0,0,-1]) {
        cylinder(h=depth+1, r1=shaft/2, r2=shaft/2, center=false, $fn=30);
    } // End translate

    // Cut away bottom
    translate([0,0,-5]) {
        cube([1000, 1000, 10],true);
    } // End translate

    // Cut away top
    translate([-500,-500,depth-1]) {
        cube([1000, 1000, 10],false);
    } // End translate

} // End difference

// Draw extra part in shaft hole if cut is choosen
if (cut==1) {
    translate([shaft/3,-shaft/2,0]) {
        cube([shaft/3, shaft, depth-1],false);
    } // End translate
} // End if cut

```


[User]
I'm trying to design some containers for the Vertibloom hanging planter system, but I'm having trouble finding anything that's already compatible with it. I'd like to be able to customize a few different parameters of the design, and potentially generate either closed containers or ones with drainage holes.

Here's what I'm thinking: The basic shape of the container should be a rounded rectangle. The width, depth, and height should all be customizable parameters. I'd also like to be able to adjust the radius of the rounded corners. For the drainage holes, I'm envisioning a few different sizes and positions, but they should all be on the bottom of the container. Maybe the user could define an array of `[x_position, y_position, diameter]` tuples to specify where those holes should be placed. For the `x` and `y` positions, maybe -1 to 1 could represent the entire span of the basket, so `-0.5` would be halfway between the center and left edge, for example.

I want the top of the container to have a small lip or brim. The width and height of this brim should also be adjustable. There should be a way to scale the entire container, and a separate parameter to scale the projection of the rounded portion on the bottom.

For the Vertibloom compatibility, I need to incorporate hanging pegs into the design. I'll need parameters for the stem depth, height, and width of these pegs, as well as parameters for the lock depth, scale, and width. The overhang of the lock should be calculated based on the lock width and stem width. Let's use `clip_overhang=(clip_lock_width-clip_stem_width)/2`, for example. The total height of the clip should be the stem height plus twice the overhang, and the total depth and width of the clip should be the sum of the stem and lock depths, and the lock width, respectively. We should be able to position the pegs using parameters for their height and horizontal position, and also specify how many pegs to use and the spacing between them both horizontally and vertically.

To keep things compatible with the Vertibloom system, the `clip_*_spacing`, `clip_lock`, and `clip_stem` parameters should not be changed frequently. There will likely be parameters that the user will want to adjust all the time, and others that should be changed very rarely or not at all. The documentation should note which parameters are which.

I think it would be really helpful to have some helper modules for transformations. Maybe modules for rotating around each axis (`xrotate`, `yrotate`, `zrotate`), translating along each axis (`xmove`, `ymove`, `zmove`), and spreading objects along each axis with a given spacing (`xspread`, `yspread`, `zspread`). The rotate and move modules could simply call a module that takes an axis name, or a generic module that accepts a rotation vector.

I'd also like to have a module for creating the basic shape of the container, one for creating the base with or without drainage holes, one for creating the brim, and one for the hanging pegs.

Finally, the main module should combine all these elements to create the complete container.

Could you help me design a parametric OpenSCAD file for this? I'd like to use the MCAD library for this project.

[Assistant]
basket.scad:
```
basket_width=75;
basket_depth=60;
basket_height=80;
basket_scale=1.25;
basket_projection_scale=0.25;
basket_round_radius=5;

basket_brim_width=2;
basket_brim_height=2;

wallwidth=1.8;

clip_stem_depth=7.1;
clip_stem_height=11;
clip_stem_width=3;
clip_lock_depth=2;
clip_lock_scale=0.8;
clip_lock_width=5.4;
clip_overhang=(clip_lock_width-clip_stem_width)/2;
clip_height=clip_stem_height + clip_overhang*2;
clip_depth=clip_stem_depth + clip_lock_depth;
clip_width=clip_lock_width;

clip_z=48.7;
clip_x=0;
clips_wide=1;
clips_high=1;
clips_x_spacing=88;
clips_z_spacing=62.5;

basket_full_depth=basket_depth+2*basket_round_radius;
basket_full_width=basket_width+2*basket_round_radius;

drainage=false;
base_holes=[
    [0, 0.3, 10],
    [-0.5, -0.5, 8],
    [-0.5, 1, 8],
    [0.5, -0.5, 8],
    [0.5, 1, 8]
];

module rotate_axis(axis, d) {
    rotate(list_for_axis(axis, d)) children();
}

module xrotate(d) {
    rotate_axis("x", d) children();
}

module yrotate(d) {
    rotate_axis("y", d) children();
}

module zrotate(d) {
    rotate_axis("z", d) children();
}

function list_for_axis(axis, d) =
(
    axis=="x" ? [d, 0, 0] : (
        axis=="y" ? [0, d, 0] : (
            axis=="z" ? [0, 0, d] : [0, 0, 0]))
);

module translate_axis(axis, d) {
    translate(list_for_axis(axis, d)) children();
}

module xmove(d) {
    translate_axis("x", d) children();
}

module ymove(d) {
    translate_axis("y", d) children();
}

module zmove(d) {
    translate_axis("z", d) children();
}

module spread_axis(axis, d, n=1) {
    x = d/2;
    if(d==0 || n==0) {
        children();
    }
    else
    {
        for(i=[-x:(d/n):x]) {
            translate_axis(axis, i) children();
        }
    }
}

module xspread(d, n=1)
{
    spread_axis("x", d, n) children();
}

module yspread(d, n=1)
{
    spread_axis("y", d, n) children();
}

module zspread(d, n=1)
{
    spread_axis("z", d, n) children();
}

module keyhole() {
    xmove(-3.5/2) square([3.5, 9]);
    xmove(-3) ymove(9) square([6, 15]);
}

// size: [clip_width, clip_depth]
module clip() {
    ymove(-clip_lock_depth/2) {
        cube(size=[clip_stem_width, clip_stem_depth, clip_stem_height], center=true);
        ymove(clip_stem_depth/2) xrotate(90) mirror([0,0,1]) linear_extrude(clip_lock_depth, scale=clip_lock_scale) offset(clip_overhang) square(size=[clip_stem_width, clip_stem_height], center=true);
    }
}

module basket_shape() {
    union() {
        offset(basket_round_radius) square(size=[basket_width, basket_depth], center=true);

        ymove(basket_depth/2+basket_round_radius/2) square(size=[basket_full_width, basket_round_radius], center=true);
        difference() {
            ymove(basket_depth/2+basket_round_radius) scale([1, basket_projection_scale, 1]) circle(basket_full_width/2, center=true, $fn=120);
            ymove(-basket_width/2-basket_round_radius) square(size=[basket_width+basket_round_radius*2, basket_width+basket_round_radius*2], center=true);
      }
    }
}

module basket_base() {
    difference()
    {
        linear_extrude(wallwidth) basket_shape();
        if(drainage) zmove(-0.1) {
            for(hole=base_holes)
            {
                xmove(hole[0]*basket_full_width/2)
                ymove(hole[1]*basket_full_depth/2) cylinder(h=wallwidth+0.2, d=hole[2]);
            }
        }
    }
}

module basket_outline() {
    difference()
    {
        basket_shape();
        offset(-wallwidth) basket_shape();
    }
}

module basket_brim() {
    difference()
    {
        scale([basket_scale, basket_scale, 1]) linear_extrude(basket_brim_height) offset(basket_brim_width/2) basket_shape();
        scale([basket_scale, basket_scale, 1]) zmove(-0.1) linear_extrude(basket_brim_height+0.2) offset(-0.1) basket_shape();
    }
}

module basket() {
    xspread(clips_x_spacing, n=clips_wide-1) zspread(clips_z_spacing, n=clips_high-1) zmove(clip_z) ymove(-clip_depth/2) zrotate(180) clip();
    linear_extrude(basket_height, scale=basket_scale) ymove(basket_full_depth/2) basket_outline();
    ymove(basket_full_depth/2) basket_base();
    zmove(basket_height-basket_brim_height) ymove(basket_full_depth*basket_scale/2) basket_brim();
}
basket();
```


[User]
I'd like to create a parametric design in OpenSCAD for a customizable, army-knife style holder for SD and microSD cards.

The holder should consist of three parts: a top, a holder, and a bottom. The top and bottom pieces will be identical except for the text on them and accommodations for screw heads and nuts.

Here's a breakdown of the design requirements:

**Dimensions and General Parameters:**

*   The overall dimensions of the holder should be approximately 29mm wide and 92mm long.
*   The external radius of the rounded ends should be 5mm.
*   The design should accommodate two M3 bolts with a diameter of 3.2mm for assembly.
*   There should be an option to include magnets for securing the holder in place. Each magnet should have a diameter of 3.3mm and a height of 1mm. The number of magnets should be a parameter, up to 3.
*   The design should also be parametric in the number of layers under the magnets, from 1 to 10.
*   The height of each layer should be 0.2mm, with the first layer having a height of 0.3mm.
*   There should be small offsets of 0.3mm for both the x and y dimensions of the card slots to ensure a good fit.

**Card Slots:**

*   The holder should have two primary slots. Each slot can be configured to hold either one SD card or four microSD cards.
*   The dimensions for the SD card slot should be approximately 24mm x 32mm.
*   The dimensions for the microSD card slots should be approximately 11mm x 15mm each.
*   There should be an option to include a semi-circular cutout with a diameter of 19mm for the SD card and 9mm for the microSD cards to aid in pushing the cards out.
*   There should be another option to have a cutout for pulling the holder open, with the size of the cutout being a parameter.

**Structure:**

*   The base of each part should be a rounded rectangle, created by the hull of four cylinders.
*   The top part should have countersinks for the screw heads, while the bottom part should have hexagonal grooves for the nuts.
*   The middle part should have the card slots and a central cutout to make it lighter and easier to rotate.
*   There should be an option to include a spacer in the central cutout.
*   There should be holes for the magnets, with the position and number being parametric.

**Text:**

*   The top part should have the text "Memory Cards" centered on the top, and the bottom part should have the text "HackerMagnet" on the bottom, both using the "Spin Cycle OT" font.
*   The text should be extruded to a height of 0.4mm.

**Libraries:**

The design should use the "text\_on" library for text extrusion. I think that's the only external library I'll need - if that's not right, please correct the code accordingly.

Could you help me create a parametric OpenSCAD file that meets these requirements? I want to be able to customize the holder through the OpenSCAD customizer to adjust the number of magnets, layers, types of card slots, and the presence of the push cutouts and spacer.

[Assistant]
uSD_holder.scad:
```
//SD/microSD pocket customizable (army knife style)
//by Yannis Kari
//ToDo: add minisd slot

use <text_on/text_on.scad>

part = 2; // [1:top, 2:holder, 3:bottom]

ext_radius = 5;
size_x = 29;
size_y = 92;
hole_dia = 3.2;
magnet_dia = 3.3;
magnet_height = 1;
magnets = 3;
layers = 3; // layers below magnets 1...10
layer_height = 0.2;
first_layer_height = 0.3;
offset_x = 0.3;
offset_y = 0.3;

slot1 = 1; // [1:SD, 2:4x microSD]
slot2 = 2; // [1:SD, 2:4x microSD]
finger_push = 0; // [1:Yes, 0:No]
finger_pull = 1; // [1:Yes, 0:No]
spacer = 1; // [1:Yes, 0:No]

//Hidden
$fn = 60;
//Calc
magnet_h = magnets * magnet_height;
size_z = magnet_h + first_layer_height + (layers-1)*layer_height;

module SD(){
	translate([-offset_x/2,-offset_y/2,0])cube([24+offset_x,32+offset_y,2.5+0.1]);
	if (finger_push == 1) translate([24/2,32/2,-size_z+2.5-0.1])cylinder(d=19,h=size_z-2.5+0.2);
}

module microSDx4(){
	translate([-offset_x/2,-offset_y/2,0])cube([11+offset_x,15+offset_y,2.5+0.1]);
	translate([11+2-offset_x/2,-offset_y/2,0])cube([11+offset_x,15+offset_y,2.5+0.1]);
	translate([-offset_x/2,15+2-offset_y/2,0])cube([11+offset_x,15+offset_y,2.5+0.1]);
	translate([11+2-offset_x/2,15+2-offset_y/2,0])cube([11+offset_x,15+offset_y,2.5+0.1]);
	if (finger_push == 1){
		translate([11/2,15/2,-size_z+2.5-0.1])cylinder(d=9,h=size_z-2.5+0.2);
		translate([13+11/2,15/2,-size_z+2.5-0.1])cylinder(d=9,h=size_z-2.5+0.2);
		translate([11/2,17+15/2,-size_z+2.5-0.1])cylinder(d=9,h=size_z-2.5+0.2);
		translate([13+11/2,17+15/2,-size_z+2.5-0.1])cylinder(d=9,h=size_z-2.5+0.2);
	}
}

module base(thick){
	hull(){
		translate([ext_radius,ext_radius,0])cylinder(d=ext_radius*2,h=thick);
		translate([ext_radius,size_y-ext_radius,0])cylinder(d=ext_radius*2,h=thick);
		translate([size_x-ext_radius,size_y-ext_radius,0])cylinder(d=ext_radius*2,h=thick);
		translate([size_x-ext_radius,ext_radius,0])cylinder(d=ext_radius*2,h=thick);
	}
}

module screw_holes(thick){
	translate([size_x-5.5,5.5,-0.1])cylinder(d=hole_dia,h=thick+0.2);
	translate([5.5,size_y-5.5,-0.1])cylinder(d=hole_dia,h=thick+0.2);
}

module screw_heads(thick){
	translate([size_x-5.5,5.5,thick-3])cylinder(d=5.7,h=3+0.1);
	translate([5.5,size_y-5.5,thick-3])cylinder(d=5.7,h=3+0.1);
}

module magnet_holes(thick){
	translate([5.5,5.5,size_z-magnet_h])cylinder(d=magnet_dia,h=magnet_h+0.1);
	translate([29-29/4,size_y-5.5,0.7])cylinder(d=magnet_dia,h=thick-0.7+0.1);
}

module nut_grooves(thick){
	translate([size_x-5.5,5.5,-0.1])rotate([0,0,45])cylinder(d=6.8,h=2.5+.1,$fn=6);
	translate([5.5,size_y-5.5,-0.1])rotate([0,0,45])cylinder(d=6.8,h=2.5+0.1,$fn=6);
}

module print_part(type){
	difference(){
		union(){
			if(type == 2){
				base(size_z);
				translate([size_x-ext_radius,size_y-ext_radius])cube([ext_radius,ext_radius,size_z]);
				difference(){
					translate([size_x-size_x/4,size_y,0])cylinder(d=29/2,h=size_z);
					if (finger_pull) translate([size_x-size_x/4,size_y,size_z])rotate([-15,0,0])cylinder(d=29/2-4,h=size_z);
				}
			}
			else base(size_z);
		}
		if (type == 2){
			if (slot1 == 1) translate([2.5,13,size_z-2.5]) SD();
			else if (slot1 == 2) translate([2.5,13,size_z-2.5]) microSDx4();
			if (slot2 == 1) translate([2.5,47,size_z-2.5]) SD();
			else if (slot2 == 2) translate([2.5,47,size_z-2.5]) microSDx4();
			difference(){
				union(){
					translate([size_x/2-22/2,size_y,-0.1])cylinder(d=22,h=size_z+0.2);
					translate([-0.1,size_y-11,-0.1])cube([size_x/2-11+0.1,11,size_z+0.2]);
				}
				if (spacer){
					translate([size_x/2-22/2,size_y,-0.1])cylinder(d=17,h=size_z+0.2);
					translate([-0.1,size_y-8.5,-0.1])cube([size_x/2-11+0.1,8.5,size_z+0.2]);
				}
			}
			screw_holes(size_z);
			magnet_holes(size_z);
		}
		else{
			screw_holes(size_z);
			magnet_holes(size_z);
			if (type == 1){
				screw_heads(size_z);
				translate([size_x/2,size_y/2,size_z-0.2/2])rotate([0,0,90])text_extrude("Memory Cards",extrusion_height=0.4+0.1,size=6,font="Spin Cycle OT",center=true);
			}
			else{
				nut_grooves();
				translate([size_x/2,size_y/2,0.2/2])rotate([180,0,90])text_extrude("HackerMagnet",extrusion_height=0.4+0.1,size=6,font="Spin Cycle OT",center=true);
			}
		}
	}
}

if (part == 3) translate([size_x,0,size_z])rotate([0,180,0]) print_part(part);
else print_part(part);
```


[User]
I need you to create a parametric OpenSCAD design for a customizable tray. I want to be able to use this for a variety of purposes, so it needs to be highly adaptable.

**Basic Shape and Dimensions:**

*   The core of the design should be a box. I need to be able to set the width, depth, and height of this box. Let's set the ranges as follows:
    *   **Width:** 24mm to 144mm
    *   **Depth:** 24mm to 144mm
    *   **Height:** 8mm to 80mm
*   The box should have rounded corners. I don't want sharp edges. I'd like you to use the `roundedBox` function from the **MCAD** library.

**Drainage Options:**

I want to have options for ensuring proper drainage. Here's what I have in mind:

1. **Ventilation Fins:**  This is especially important if I use this as a soap tray. I need an option to include ventilation fins on the bottom. These fins should be a series of parallel, raised ridges that allow water to drain away from the soap. Since this design is parametric, these should adjust their position based on the dimensions of the tray. The user should be able to toggle these fins on or off.
2. **Drainage Holes:**  Another option should be to include drainage holes in the bottom of the tray. The holes should be evenly spaced and their size should be adjustable, or at least scale reasonably with the dimensions of the tray. These should also be togglable.

**Mounting Option:**

Finally, I'd like to have the option to mount this tray onto a cylindrical tube. Here's how that should work:

*   **Tube Mount:** The user should be able to toggle a "mount" option. If enabled, a cylindrical support structure should be added to the underside of the tray.
*   **Customizable Diameter:** The user should be able to specify the diameter of the tube they want to mount the tray on. This diameter should be adjustable between 6mm and 40mm.
*   **Secure Fit:** The mount should be designed in such a way that it securely clips onto the tube. This will likely involve some cutouts or slots in the cylindrical support to allow it to flex and grip the tube.

**In summary,** I want a parametric OpenSCAD file that lets me create a customizable tray with options for rounded corners, ventilation fins, drainage holes, and a tube mount. Please ensure the design is well-commented and easy to understand. Let me know if you have any questions.

[Assistant]
CustomTray2.scad:
```
// preview[view:north east, tilt:top]
include <MCAD/boxes.scad>  // Include Boxes library to get easy rounded boxes

// Width of box
width = 96; // [24:12:144]

// Depth of box
depth = 84; // [24:12:144]

// Height of box
height = 20; // [8:80]

// Do you want ventilation fins? (your soap will last longer)
vents = 1; // [1:Yes,0:No]

// Do you want holes?
holes = 0; // [1:Yes,0:No]

// Do you want a tube mount?
mount = 0; // [1:Yes,0:No]

// Diameter of tube to mount this on
d = 17.7; // [6:0.1:40]

/* [Hidden] */

h = height+20;
r = d/2;
f = d/7;
t = d/6;
os = t;
a = 44;
detail = 40;

// Difference will construct a main shape and subtract cut outs
difference() {

  //***********
  // Main Shape
  //***********

  union(){

    // Main mount cylinder solid
    if (mount == 1) {
      translate([0,0,h/2]) {
        rcylinder(r+t,r+t,h,2,true,$fn=detail);
      } // End translate
    } // End if mount

    // Soap cup
    difference() {

      // Soap cup solid
      translate([0,-width/2-t-os,height/2]) {
        roundedBox([depth, width, height], 4, false, $fn=40);
      }

      // Soap cup cut out
      translate([0,-width/2-t-os,(height/2)+width/25]) {
        roundedBox([depth-width/11, width-width/11, height], 4, false, $fn=40);
      }

      // Holes in the soap cup
      if (holes == 1) {
        for (spaceD =[-depth/2+12:12:depth/2-3]) {
          for (spaceW =[r+10:12:width-1]) {
            translate([spaceD,-spaceW+2.5-os,-1]) {
              cylinder(width/10, 3, 5, false, $fn=detail);
            } // End translate
          } // End for spaceW
        } // End for spaceD
      } // End if holes

    } // End difference soap cup

    // Ventilation fins
    if ((vents==1) && (depth>=48) && (width>=48)) {
      difference() {

        union(){

          // Big fins
          rotate([90,0,180]) {
            for (spaceW =[r+10+4.5:12:width-5]) {
              translate([-depth/3.8,width/15,-spaceW+1-os]) {
                linear_extrude(height = 2, center = true, convexity = 10, twist = 0)
polygon(points=[[0,0], [0,-3], [depth/1.5,-3], [depth/1.5,40],[depth/1.5-5,40]]);
              } // End translate
            } // End for spaceW
          } // End rotate

          // Small fins
          rotate([90,0,0]) {
            for (spaceW =[r+10+4.5:12:width-5]) {
              translate([depth/7-1,width/15,spaceW-1+os]) {
                linear_extrude(height = 2, center = true, convexity = 10, twist = 0)
polygon(points=[[0,0],[0,-3],[depth/3.5,-3],[depth/3.5,15],[depth/3.5-5,15]]);
              } // End translate
            } // End for spaceW
          } // End rotate
        } // End union
    } // End difference
  } // End if vents

  } // End union

  // Cut outs from mounting cylinder

  if (mount==1) {
    // Cut out center cylinder
    translate([0,0,-1]) {
      cylinder(h+2, r, r, false, $fn=detail);
    }

    // Cut out right wall
    rotate([0,0,a]) {
      translate([0,r,h/2]) {
        cube([t, d, h+2],true);
      }
    }

    // Cut out left wall
    rotate([0,0,-a]) {
      translate([0,r,h/2]) {
        cube([t, d, h+2],true);
      }
    }

    // Cut out center wall
    translate([0,r,h/2]) {
      cube([d/1.18, d, h+2],true);
    }
  }
} // End difference

  module rcylinder(r1=10,r2=10,h=10,b=2)
{translate([0,0,-h/2]) hull(){rotate_extrude() translate([r1-b,b,0]) circle(r = b); rotate_extrude() translate([r2-b, h-b, 0]) circle(r = b);}}

```


[User]
Hi, I'm looking to design a parametric nautilus shell in OpenSCAD. I've seen a few attempts online, and they all seem to have similar approaches but I'm hoping you can create something more robust, especially for rendering in the Customizer or for larger designs. The main issue seems to be that the existing code generates shells by subtracting a smaller series of nested spheres from a larger one, and the thickness of the shell is not constant, which I'd like to fix. Also, cutting the shell in half using a cube is okay, but I'd prefer a built-in parameter for that.

Here's what I'm looking for:

1.  **Parametric Shell Generation:** Instead of relying on nested spheres and a difference operation, I think it'd be better to generate the shell by defining a profile curve and revolving it. This should be done parametrically, allowing control of the profile shape, how much the radius expands per rotation, and the total number of rotations.

2.  **Constant Thickness:** The biggest issue I have with existing designs is that the shell thickness varies. I would really like it to remain consistent. I suspect this can be achieved by extruding the profile curve directly.

3.  **Customizable Parameters:** I want to be able to control these parameters:
    *   `circle_fraction`: Number of sides to approximate a circle (like `$fn`).
    *   `steps_per_rotation`: Number of steps/points around the profile curve.
    *   `windings`: Number of rotations/windings of the shell.
    *   `expansion_factor`: How quickly the shell's radius increases per rotation.
    *   `shell_thickness`: The thickness of the shell.
    *   `cut_in_half`: A boolean parameter to cut the shell in half if true, and not if false.
    *   `opening_position`: An angle (in degrees) indicating the position of the opening along the shell. (If zero it would align with where the shell initially winds from)

4.  **Opening:** I'd like to have an opening at the end of the shell that can be controlled using the `opening_position` parameter. Instead of just using a subtraction, it would be preferable to have a profile or function that creates an opening with a smooth transition.

5.  **No reliance on libraries** I'm not really looking to incorporate any libraries like MCAD or BOSL, as this is meant to be a simple design.

6.  **Customizer Friendly:** The design should be as Customizer-friendly as possible. I'd like to avoid the crashing issues I've seen in other shells. If it can avoid excessive CSG operations or complex subtractions that would be helpful.

Could you generate an OpenSCAD file that does all of that?

[Assistant]
Nautilus2.scad:
```
/*
 Minimalistic nautilus shell by Tycho
 thing:1773803 Version 2

 More side is better but it will take ages to render.
 Thingyverse will crash if this value is too high.
*/

// [Details]
// number of side to make a circle
circle_fraction=12;// [6:60]
// More steps is smoother surface
stepspercircle = 19; // [2:24]
// Up to 6 should work. After this you have te replace the position of the opening.
windings = 6; // [2:10]
// This is a fraction of the size of the spheres
outer=1.75;
// Shell thickness
thickness=1.22;
/* [Hidden] */
// preview[view:"north east", tilt:"top diagonal"]

$fn=circle_fraction;

$vpt=[-12, 0, 77 ];
$vpr=[ 40, 0.00, 92.0 ];

module snailhouse(scale=1.75, thickness=0){
    for (i = [0.5:1/stepspercircle:windings]){
        rotate(-i*360)
        translate([0,(pow(2.1,i)-2),(pow(2,i)/2)-2])
            sphere( ((pow(2,i)/scale)-thickness), center=true);

    }
}

difference(){
    snailhouse(outer);
    union(){
        snailhouse(scale=outer, thickness=thickness);
        // For an opening:
            translate([+0.5,pow(2.1,windings)-1,(pow(2,windings)*0.5)])
                sphere(d=pow(2,windings)/1, center=true);
//        // Cut in half:
//        translate([0,0,40])
//            cube([100,100,50], center=true);
    }
}
```
Nautilus.scad:
```
// Minimalistic nautilus shell by Tycho
// thing:1773803
// To render, best use command line or be very patient. This SCAD is simple but hard to render.

// Thingiverse will crash if this value is too high.
$fn=12; // [8:42]
// More steps is smoother surface
stepspercircle = 19; // [2:24]
// Up to 6 should work. After this you have te replace the position of the opening.
windings = 3; // [2:6]
// This is a factor of the size of the spheres
outer=2.5;
inner=3;
// preview[view:"north east", tilt:"top diagonal"]

module snailhouse(scale=1.75){
    for (i = [0.5:1/stepspercircle:windings]){
        rotate(i*360)
        translate([0,(pow(2,i)-1.2)])
            sphere((pow(2,i)/scale));

    }
}

difference(){
    snailhouse(outer);
    union(){
        snailhouse(inner);
        // For an opening:
            translate([-1,pow(2,windings)-1.2])
                cylinder(d=pow(2,windings)/1.5, h=pow(2,windings), center=true);
//        // Cut in half:
//        translate([0,0,25])
//            cube([200,200,50], center=true);
    }
}
```
Nautilus_heavy.scad:
```
// Minimalistic nautilus shell by Tycho
// thing:1773803
// To render CSG best use command line or be very patient.

// More side is better but it will take ages to render.
// Thingyverse will crash if this value is too high.
$fn=42; // [8:42]
// More steps is smoother surface
stepspercircle = 23.5; // [2:24]
// Up to 6 should work. After this you have te replace the position of the opening.
windings = 4; // [2:6]
// This is a factor of the size of the spheres
outer=2.4;
inner=3;
// preview[view:"north east", tilt:"top diagonal"]

module snailhouse(scale=1.75){
    for (i = [0.5:1/stepspercircle:windings]){
        rotate(i*360)
        translate([0,(pow(2,i)-1.2)])
            sphere((pow(2,i)/scale));

    }
}

difference(){
    snailhouse(outer);
    union(){
        snailhouse(inner);
        // For an opening:
            translate([-1,pow(2,windings)])
                cylinder(d=pow(2,windings)/1.5, h=pow(2,windings), center=true);
//        // Cut in half:
//        translate([0,0,25])
//            cube([200,200,50], center=true);
    }
}
```


[User]
I'm looking to create a customizable shelving system that can grow and adapt to different spaces. I really like the concept in the provided description, using a set of interlocking tiles that can be combined in various ways to build shelving units. The idea is inspired by Wang tiles and uses pins to connect the tiles, and glue for a strong connection.

I want a parametric design that allows me to control the grid size, wall thickness, and the height of the shelves. I need to be able to generate five different tile types: a corner tile, an end tile, a straight tile, a T-tile, and a cross tile, as well as a 90 degree angle connector. The tiles should all be based on the same grid size. The pin design should also be parametric, allowing adjustments to their radius and height.

The goal is to have the flexibility to create different sized shelves that can expand both horizontally and vertically. The design should be clean and easy to print, with the pins providing good alignment and structural support. Can you create an OpenSCAD file (or files) that implements this? I would expect something that uses modules for each of the shapes.

[Assistant]
growing_shelf_customizer.scad:
```
/*
GROWING SHELF CUSTOMIZER

Adapted for Thingiverese customizer

Fernando Jerez 2016

*/

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

/* [Generated pieces] */
size = 2; // [1:1x grid size, 2:2x grid size, 3:3x grid size, 4:4x grid size]
height = 1; // [1:1x grid size, 2:2x grid size, 3:3x grid size, 4:4x grid size]

// Which one would you like to see?
part = "all"; // [all:all pieces, corner:Corner, one:end tile, two: straight tile, three:T-tile, four: Cross tile, angle: 90 deg angle]

/* [Grid config] */
grid_size = 40;
wall_thickness = 10;

/* [Hidden] */
grid = grid_size*size;

altura = grid_size;
levels = height;
grosor = wall_thickness;

pinradius = 2.5;
pinheight =10;
$fn=60;

// Show parts
union(){
    for(i=[0:levels-1]){
        translate([0,0,altura*i]){
            if(part=="all"){
                complete_set();
            }else if(part=="corner"){
                tileCorner();
            }else if(part=="one"){
                tile1();
            }else if(part=="two"){
                tile2();
            }else if(part=="three"){
                tile3();
            }else if(part=="four"){
                tile4();
            }
        }
    }
    if(part=="angle"){
        angle(90);
    }
    if(part=="all"){
        t = grid*1.5;
        translate([-t*1.25,0,-altura/2]) angle(90);
        translate([-t,-t,0])
        linear_extrude(height=1)
        text(text=str("All parts:",grid,"mm x ",(altura*levels),"mm"),size=14);
    }

}

//angle(90);
module angle(ang){
    union(){
    for(i=[0:levels-1]){
        difference(){
            rotate([90,0,0]) arc([altura*(i),altura*(i+1),grosor],0,ang);

            rotate([0,90-ang,0]) translate([0,-grosor*0.5,altura*(i)+altura*0.5]){
                translate([0, 0,-altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                translate([0, 0,altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
            }
            rotate([0,90,0]) translate([0,-grosor*0.5,altura*(i)+altura*0.5]){
                translate([0, 0,-altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                translate([0, 0,altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
            }
        }
    }
}
}

module complete_set(){
    t = grid*1.5;
    tile4();
    translate([t,0,0]) tile3();
    translate([0,t,0]) tileCorner();
    translate([t,t,0]) tile2();
    translate([-t,t,0]) tile1();
}

module tile1(){
    union(){
        difference(){
            translate([-grid*0.25,0,0]) cube([grid*0.5,grosor,altura],center = true);
                rotate([0,0,180]){
                    translate([grid*0.5,0,-altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                    translate([grid*0.5,0,altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                }
        }
        cylinder(r=grosor/2,h=altura,center = true);
    }
}

module tile2(){
    difference(){
        cube([grid,grosor,altura],center = true);
        for(i=[0:180:180]){
            rotate([0,0,i]){
                translate([grid*0.5,0,-altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                translate([grid*0.5,0,altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
            }
        }
    }
}

module tileCorner(){
    intersection(){
        cube([grid,grid,altura],center = true);
        difference(){
            translate([-grid/2,-grid/2,0]) cylinder(r=grid/2+grosor/2,h=altura,center = true);
            translate([-grid/2,-grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);
            for(i=[180:90:270]){
                rotate([0,0,i]){
                    translate([grid*0.5,0,-altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                    translate([grid*0.5,0,altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                }
            }
        }
    }
}

module tile3(){
    difference(){
        translate([0,(-grid+grosor)*0.25,0])
        cube([grid,grid/2+grosor/2,altura],center = true);
        translate([grid/2,grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);
        translate([grid/2,-grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);
        translate([-grid/2,grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);
        translate([-grid/2,-grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);

        //cylinder(r=grid/5,h=altura+0.01,center = true);

        for(i=[180:90:360]){
            rotate([0,0,i]){
                translate([grid*0.5,0,-altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                translate([grid*0.5,0,altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
            }
        }

    }
            //cube([grid-pinheight,grosor,altura],center = true);

}
module tile4(){

    difference(){
        cube([grid,grid,altura],center = true);
        translate([grid/2,grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);
        translate([grid/2,-grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);
        translate([-grid/2,grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);
        translate([-grid/2,-grid/2,0]) cylinder(r=grid/2-grosor/2,h=altura+0.01,center = true);

        cylinder(r=grid/6,h=altura+0.01,center = true);

        for(i=[0:90:270]){
            rotate([0,0,i]){
                translate([grid*0.5,0,-altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
                translate([grid*0.5,0,altura*0.25]) rotate([0,90,0]) cylinder(r=pinradius,h=pinheight,center = true,$fn=20);
            }
        }

    }
}

module arc(size, a1,a2){
    rout = max(size[0],size[1]);
    rin = min(size[0],size[1]);
    altura = size[2];
    ang1 = min(a1,a2);
    ang2 = max(a1,a2);

    if(ang2-ang1>=360){
        difference(){
            cylinder(r=rout,h=altura);
            translate([0,0,-0.05]) cylinder(r=rin,h=altura+0.1);
        }
    }else{
        ang = (ang2-ang1) / $fn;

        pout = [ for (i = [ 0 : $fn ]) let (x1 = rout*cos(ang1+i*ang),y1 = rout*sin(ang1+i*ang)) [ x1,y1 ] ];
        pin = [ for (i = [ 0 : $fn ]) let (x1 = rin*cos(ang1+i*ang),y1 = rin*sin(ang1+i*ang)) [ x1,y1 ] ];

        puntos = [for (i = [ 0 : $fn*2+1 ]) (i<=$fn) ? pout[i] : pin[$fn+1-( (i-$fn) )] ];

        linear_extrude(height=altura) polygon(points=puntos);
    }

}
```


[User]
I'm looking to create a customizable hydroponic tray system and would like your help generating the OpenSCAD code. I need a tray made up of individual, connected modules. Here's what I'm envisioning:

The basic module should be a frustum, essentially a square pyramid with the top cut off, but with four sides rather than being truly pyramidal (imagine a tapered square vase). I need to be able to adjust the top and bottom side lengths of each module, as well as the overall module height and the wall thickness. I'll also want to control how many modules are in the X and Y directions, so the overall tray size will be adjustable.

For water management, I need two optional features that can be toggled on or off:

First, I need the ability to add a small drain hole at the bottom of each module. The diameter of this hole should be adjustable. Second, I need an optional integrated irrigation system that runs between the modules. If enabled, it should run along the length of the whole tray in the X direction (not in the Y), and it should be configurable in size and thickness. This should be essentially a hollow, rectangular channel with periodic openings to feed water into each module directly.

So, in summary, I'd need the following parameters to be configurable:

*   `Thickness`: The wall thickness of the modules.
*   `ModuleSideSizeTop`: The side length of the square top of a module.
*   `ModuleSideSizeBottom`: The side length of the square bottom of a module.
*   `ModuleHeight`: The height of a module.
*   `NumberModulesX`: The number of modules in the X direction.
*   `NumberModulesY`: The number of modules in the Y direction.
*   `ScapeHole`: A boolean to turn the drain hole on or off.
*   `ScapeHoleDiameter`: The diameter of the drain hole.
*   `IrrigationSystem`: A boolean to turn the irrigation system on or off.
*   `IrrigationSystemSize`: The size of the irrigation channel (the square cross section width/height).
*   `IrrigationSystemThickness`: The thickness of the walls of the irrigation channel.

I expect to use the standard OpenSCAD `cylinder` function for both frustums and the holes. I would also like to avoid using any external libraries for this, if possible. I think that's everything, thanks for your help!

[Assistant]
Tray.scad:
```
/* [General] */
Thickness = 1;
ModuleSideSizeTop=20;
ModuleSideSizeBottom=10;
ModuleHeight = 30;
NumberModulesX = 3;
NumberModulesY = 4;

/* [Water] */
ScapeHole = "false"; //[true, false]
ScapeHoleDiameter = 1.5;
IrrigationSystem = "true"; //[true, false]
IrrigationSystemSize = 2.4;
IrrigationSystemThickness = 0.7;

/* [HIDDEN] */
sqr2 = sqrt(2);
$fn=30;

vase();

module vase(){
    tkDiv = 4;
    stepX = ModuleSideSizeTop-Thickness/tkDiv;
    stepY = ModuleSideSizeTop-Thickness/tkDiv;
    for(x=[0:NumberModulesX-1])
        for(y=[0:NumberModulesY-1])
            translate([x*stepX,
                       y*stepY])
                modulo();
    if(IrrigationSystem == "true"){
        translate([Thickness/2/tkDiv,0,-IrrigationSystemThickness/2])
        for(x=[1:NumberModulesX-1])
            translate([x*stepX-IrrigationSystemSize/2-
                        ModuleSideSizeTop/2,
                       -ModuleSideSizeTop/2,ModuleHeight])
                difference(){
                    iySize = NumberModulesY*stepY+Thickness/tkDiv;
                    cube([IrrigationSystemSize,
                          iySize,
                          IrrigationSystemSize]);
                    translate([IrrigationSystemThickness,
                               IrrigationSystemThickness,
                               IrrigationSystemThickness])
                        cube([IrrigationSystemSize-IrrigationSystemThickness*2,
                              iySize-IrrigationSystemThickness*2,
                              IrrigationSystemSize]);
                    for(y=[0:NumberModulesY-1]){
                        translate([-IrrigationSystemSize/2,
                                  y*(ModuleSideSizeTop-Thickness/2)+ModuleSideSizeTop/2,
                                  IrrigationSystemThickness])
                            cube([IrrigationSystemSize*2,Thickness,IrrigationSystemSize*2]);
                    }
                }
    }
}
module modulo(){
    rotate(a=[0,0,45]) difference(){
        cylinder(d1=ModuleSideSizeBottom*sqr2,d2=ModuleSideSizeTop*sqr2,h=ModuleHeight,$fn=4);
        translate([0,0,Thickness])
            cylinder(d1=(ModuleSideSizeBottom-Thickness*2)*sqr2,
                     d2=(ModuleSideSizeTop-Thickness*2)*sqr2,h=ModuleHeight,$fn=4);
        if(ScapeHole == "true"){
            cylinder(d=ScapeHoleDiameter,h=Thickness*3,center=true);
        }
    }
}
```


[User]
I'm trying to create a fractal-like structure in OpenSCAD, and I have a basic idea working but I need a more robust and parametric solution.

Right now, my code takes a set of 2D points and draws lines between them. Then, recursively, it scales and rotates a copy of the original shape into each of those lines.  It uses `hull()` to connect the points at the final iteration.  Here's what I want the final design to be able to do:

1.  **Arbitrary Base Shape:** Instead of hardcoding points in a list, I'd like to be able to define the initial shape using a function. It should be flexible so I can make it a triangle, a square, a pentagon, or even more complex shapes using some formula or a simple list of points.  This way I won't need to edit the array directly. I'd like to be able to specify the points using a 2D array.
2.  **Parametric Iterations:**  I want to control how many iterations of the fractal are generated. This should be a simple integer.
3.  **No Y-Zero Requirement:** My current code is limited because the starting and ending y-values of my original shape must be zero. The fractal generation math needs to be generalized for arbitrary 2D starting and ending points.
4.  **Scaling Factor:** I need to be able to specify a scaling factor at each iteration, instead of scaling to the total length.  For example, I might want each iteration to shrink by only 75%, or expand by 110%. This scaling should be applied in both the X and Y directions equally.
5.  **Rotation Control:** While my code does the rotation automatically to align with the segment, I also need a method of applying an additional, optional, rotation at each level, which could be different at every level. This should default to zero if not specified. I'm thinking an array or a single function or a combination of the two to control this.
6.  **Smoothing Option**: If the base shape is described by a set of points, and not an explicit function, I want an option to use `hull()` as a final step to smooth the edges or to keep the points as they are.

Basically, I want to generate various fractal-like patterns with different base shapes, scaling, and rotation rules, and be able to control the complexity of the fractal as well as how many iterations there will be. I'd also like to have some flexibility with what happens at the final stage (smooth or sharp). Could you write a parametric OpenSCAD file to do that? I don't think you will need any external libraries to do this.

[Assistant]
fracttest.scad:
```
/*
Test fractal pour pas changer 😛
v0.1
*/

//points=[[0,0],[5,0],[5,5],[10,5],[10,0],[15,0],[20,0]];
//points=[[0,0],[2,2],[4,0],[6,-2],[8,0]];
//points=[[0,0],[10,0],[10,10],[0,10],[0,20],[10,20],[20,20],[30,20],[30,10],[20,10],[20,0],[30,0]];
//points=[[0,0],[20,0],[cos(-60)*20,-sin(-60)*20],[20+cos(60)*20,-sin(-60)*20],[20,0],[40,0]];
//points=[[0,0],[10,0],[10,10],[20,10],[20,0],[20,-10],[30,-10],[30,0],[40,0]];
//points=[[0,0],[20,0],[cos(-60)*20*3,-sin(-60)*20],[cos(-60)*20*2,-sin(-60)*20*2],[cos(-60)*20*3,-sin(-60)*20*3],[cos(-60)*20*5,-sin(-60)*20*3],[cos(-60)*20*6,-sin(-60)*20*2],[cos(-60)*20*5,-sin(-60)*20],[cos(-60)*20*6,0],[cos(-60)*20*8,0]];
//points=[[],[],[],[],[],[],[],[],[],[],[],[],[],[]];
points=[[0,0],[10,0],[cos(60)*30,sin(60)*10],[20,0],[30,0]];
max_iter=3;

// La variable "longueur" est égal à l'hypoténuse entre le premier point et le dernier point.
// Il y a actuellement un problème lorsque lorsque le Y du dernier point n'est pas à 0
longueur=sqrt((points[len(points)-1][0]-points[0][0])*(points[len(points)-1][0]-points[0][0])+(points[len(points)-1][1]-points[0][1])*(points[len(points)-1][0]-points[0][0]));

//toto(it=1,mit=1);
//translate([40,0,0])toto(it=1,mit=2);
//translate([80,0,0])toto(it=1,mit=3);
toto(it=1,mit=max_iter);

module toto()
{
    if(it==mit)
    {
        for(i=[0:len(points)-2])
        {
            hull()
            {
                translate([points[i][0],points[i][1],0])        // Ici se crééent les vecteurs réunissan
                sphere();                                       // les différents points.
                translate([points[i+1][0],points[i+1][1],0])    // Ils ne s'affichent que si la dernière
                sphere();                                       // itération est atteinte.
            }
        }
    }
    if(it<=mit)
    {
        for(i=[0:len(points)-2])
        {
            // Longueur2 se calcule à chaque fois et est la longueur entre le point utilisé et le point suivant.
            longueur2=sqrt(((points[i+1][0]-points[i][0])*(points[i+1][0]-points[i][0]))+((points[i+1][1]-points[i][1])*(points[i+1][1]-points[i][1])));
            // ang sert à calculer l'angle existant entre les deux points utilisés
            ang=atan2(points[i+1][1]-points[i][1],points[i+1][0]-points[i][0]);
            // On place l'objet là où il doit être
            translate([points[i][0],points[i][1],0])
            // On change sa taille pour que la taille de l'objet soit égale à la taille du vecteur
            scale([longueur2/longueur,longueur2/longueur,longueur2/longueur])
            // Et on effectue une rotation pour que l'objet rejoigne bien le point suivant
            rotate([0,0,ang])
            // On appelle l'objet en agumentation son itération
            toto(it=it+1,mit=mit);
        }
    }
}
```


[User]
Hey! I'd like a parametric version of this crystal generator. Right now it uses a `part` variable to force customizer to generate different files but the actual crystals are completely random each time. I want to be able to control things like the overall size, the number of spikes, the general shape and so on. I'd like to be able to generate a single crystal, or multiple crystals at once, based on user parameters.

Here's the original code I'm working with:

```
/*

Random crystal generator for Thingiverse Customizer

Fernando Jerez 2016

*/

// This is only to force Customizer create many files. Changes doesnt matter. Just click CREATE THING
part = "one"; // [one:Crystal #1, two:Crystal #2, three:Crystal #3, four:Crystal #4,five:Crystal #5,six:Crystal #6,seven:Crystal #7,eight:Crystal #8,nine:Crystal #9,ten:Crystal #10]

/* [Hidden] */
crystalrock();

module crystalrock(){
    union(){
        semisphere(5,$fn=6);
        for(i=[1:50]){
            angleX = 45;
            angleY = 45;
            g1=rands(-angleX,angleX,1)[0];
            g2=rands(-angleY,angleY,1)[0];

            lados=6;
            radio1=rands(1,2,1)[0];
            radio2=rands(1.1,1.6,1)[0]*radio1;
            radio3 = rands(0,0.5,1)[0]*radio2;
            altura=radio1*rands(1,2,1)[0]*5*(0.5+pow((abs(cos(g1)*cos(g2))),4));
            pico=rands(0.1,0.3,1)[0]*altura;

            rotate([g1,g2,0])
            translate([0,0,3])
            crystal(lados,radio1,radio2,radio3,altura,pico);
        }
    }
}

module crystal(lados,radio1,radio2,radio3,altura,pico){
    cylinder(r1=radio1,r2=radio2,h=altura,$fn=lados);
    translate([0,0,altura])
    cylinder(r1=radio2,r2=radio3,h=pico,$fn=lados);

}

module semisphere(r){
    difference(){
        sphere(r);
        translate([0,0,-r]) cube([2*r,2*r,2*r],center = true);
    }
}
```

Basically, I'm looking for an OpenSCAD module or set of modules that generates a crystal-like shape composed of a base semi-sphere and a number of "spikes." The current code generates these spikes randomly, but I want to control their number, angles, heights, and base radii. I'd like the number of sides on the cylinders also to be a parameter. I'd also like parameters to control the semi-sphere base, like the radius and polygon count ($fn). I think that's it - I can't wait to see what you come up with!

[Assistant]
crystal_customizer.scad:
```
/*

Random crystal generator for Thingiverse Customizer

Fernando Jerez 2016

*/

// This is only to force Customizer create many files. Changes doesnt matter. Just click CREATE THING
part = "one"; // [one:Crystal #1, two:Crystal #2, three:Crystal #3, four:Crystal #4,five:Crystal #5,six:Crystal #6,seven:Crystal #7,eight:Crystal #8,nine:Crystal #9,ten:Crystal #10]

/* [Hidden] */
crystalrock();

module crystalrock(){
    union(){
        semisphere(5,$fn=6);
        for(i=[1:50]){
            angleX = 45;
            angleY = 45;
            g1=rands(-angleX,angleX,1)[0];
            g2=rands(-angleY,angleY,1)[0];

            lados=6;
            radio1=rands(1,2,1)[0];
            radio2=rands(1.1,1.6,1)[0]*radio1;
            radio3 = rands(0,0.5,1)[0]*radio2;
            altura=radio1*rands(1,2,1)[0]*5*(0.5+pow((abs(cos(g1)*cos(g2))),4));
            pico=rands(0.1,0.3,1)[0]*altura;

            rotate([g1,g2,0])
            translate([0,0,3])
            crystal(lados,radio1,radio2,radio3,altura,pico);
        }
    }
}

module crystal(lados,radio1,radio2,radio3,altura,pico){
    cylinder(r1=radio1,r2=radio2,h=altura,$fn=lados);
    translate([0,0,altura])
    cylinder(r1=radio2,r2=radio3,h=pico,$fn=lados);

}

module semisphere(r){
    difference(){
        sphere(r);
        translate([0,0,-r]) cube([2*r,2*r,2*r],center = true);
    }
}
```