[User]
Hey, I'm looking to design a weekly pill organizer, similar to the one on Thingiverse (https://www.thingiverse.com/thing:4866368) but I want to make it parametric so I can easily adjust the size and number of compartments.

Essentially, it's a cylindrical container with a lid that clicks into place. I'd like to be able to specify:

*   The **outer diameter** of the main body.
*   The **inside height** of the body.
*   The **wall thickness**.
*   The **number of compartments** (which will translate to the number of "slices" of the cylinder), with the notches for alignment being the number of sections/compartments.
*   The **tolerance** for the click-fit between the lid and body.
*   The **angle of the "click"** for each of the lid and body alignment teeth.

The body needs to have:

*   A main cylindrical shape, with the internal space for pills,
*   Vertical walls dividing the cylinder into the desired number of compartments (equally spaced),
*   Click-fit teeth on the top of the body that match with corresponding teeth on the lid.

The lid needs to have:

*   A cylindrical shape that fits over the body,
*   Matching click-fit teeth on the inner side to secure the lid to the body,
*   A slight overhang to make it easy to grab and open.

I'd also like the option to generate a "print-ready" version that separates the lid and body for easy 3D printing. And I don't want to have to calculate the inside diameter myself - I'd like the design to do that based on the wall thickness.

I am looking for this to be implemented in OpenSCAD. I'm not too concerned about using specific libraries, but please ensure that it can be compiled in OpenSCAD without any errors.

[Assistant]
Pill_Case.scad:
```
// - Outer diameter of body
D = 60;

// - inside height of body
H = 25;

// - wall thickness
t = 2;

// - Show as packed or assembled?
print = "false"; //[true:Print version, false:Assembled]

/* [hidden] */
$fn=80;
c = 0.4;
R = D/2;
unitVol = (3.14159*pow(R-t,2) - (R-t)*t )*H /1000;
echo(unitVol, "cm^3");

//Body();
//Lid();
//if(print="true")
PrintConfig();
//else FitConfig();

module Body() {
    //body polygon
    pBody = [
        [0,         0],
        [D/2,       0],
        [D/2,       H/2-t/2],
        [D/2-t/2,   H/2],
        [D/2,       H/2+t/2],
        [D/2,       H],
        [D/2-t,     H],
        [D/2-t,     t],
        [0,         t]];
    rotate_extrude(angle=360, convexity=50) {
        polygon(pBody);
    }
    //walls
    for(i=[45:45:180]) {
        rotate(i) translate([-D/2+t/2, -t/2, 0]) {
            cube([D-t, t, H]);
        }
    }
    //filled segment. E-NE
    intersection() {
        linear_extrude(H) polygon([[0,0],[0,D],[D,D]]);
        cylinder(d=D-t,H);
    }
    //notches at E & NE
    rotate(0) translate([R-t/2,0,0]) {
        cylinder(d=t,H);
    }
    rotate(45) translate([R-t/2,0,0]) {
        cylinder(d=t,H);
    }
}

module Lid() {
    //lid polygon
    pLid = [
        [0,         H+c],
        [D/2+c,     H+c],
        [D/2+c,     H/2+t/2+c],
        [D/2-t/2,   H/2],
        [D/2+t,     H/2],
        [D/2+t,     H+c+t],
        [0,         H+c+t]];

    difference() {
        //body
        rotate_extrude(angle=45) {
            polygon(pLid);
        }
        //splits
        for(a=[45:45:360]) {
            rotate(a) translate([D/4, -c/2]) {
                cube([D,c,H*2]);
            }
        }
        //opening
        difference() {
            //triangular wedge
            linear_extrude(H*2) polygon([[0,0],[0,D],[D,D]]);
            //central cylinder
            cylinder(d=D/2,H*2);
        }

    }
}

module PrintConfig() {
    Body();
    rotate([180,0,0]) translate([D+t*2, 0, -H-t-c]) {
        Lid();
    }
}

module FitConfig() {
    intersection() {
        union() {
            Body();
            Lid();
        }
        rotate(-45/2) cube(D);
    }
}

```


[User]
I'd like to create a parametric fan adapter in OpenSCAD, and I've got some specific requirements. I need to be able to generate either a simple spacer or a full adapter that transitions between two different fan sizes. It should also include an optional grill.

Here's a breakdown of what I need:

**Fan Sizes:**

I want to support the following fan sizes, and I need the code to automatically determine the bolt hole center-to-center distance and hole diameter for each size, using these parameters:

*   25mm
*   30mm
*   35mm
*   40mm
*   45mm
*   50mm
*   60mm
*   70mm
*   80mm
*   92mm
*   120mm
*   140mm

**Adapter Types:**

*   **Spacer:** A simple spacer that provides thickness for mounting a fan with a grill (or no grill).
*   **Adapter:** A transition piece connecting a larger "adapter" size fan to a smaller "fan" size fan, with a supporting cone shape to connect the two circular parts.

**Parameters:**

*   **Adapter Type:** (Spacer or Adapter)
*   **Adapter Size:** Selectable from the list above. Must be larger than the fan size for adapters.
*   **Adapter Thickness:** Adjustable.
*   **Adapter-Fan Gap:** An adjustable gap for adapters between the mounting face for the adapter side and the mounting face of the fan side. The script should automatically adjust the gap so it's equal to or greater than half the difference in size between the adapter and the fan. This is mostly used for determining the height of the supporting cone for adapter type.
*  **Fan Size:** Selectable from the list above.
*   **Fan Spacer Thickness:** Adjustable thickness for the fan side.
*   **Minimum Border:** The minimum width of material between the edge of the mounting surface for the fan and the edge of the circular cutout within the mount.
*   **Fan Rotation:** Allow rotation of the fan mounting holes to avoid intersection with the adapter's mounting holes.
*   **Grill Type:** (None, Line, Circular)
*   **Grill Line Size:** Adjustable size for the grill lines.
*   **Grill Line Thickness:** Adjustable thickness for grill lines.
*   **Grill Space Factor:** Adjustable spacing factor for lines or rings in grill, which scales with the grill line size for even spacing.

**Grill Details:**

*   **Line Grill:**  A grid of lines across the fan opening. For the y axis, for fan sizes under 80mm, I only want one set of lines and for fan sizes over 80mm, I want two sets spaced equidistant from the center.
*   **Circular Grill:** A series of concentric rings.

**Construction:**
For all parts, the script should:
* Start with the fan sizes and associated bolt hole diameter and spacing, and make those accessible as parameters.
* The `adapter` module should create the adapter with the cone shape.
* The `spacer` module should create the spacer, along with the grill if requested.
* The code should use `difference()` with `hull()` to create the main shapes, and `cylinder()` primitives for the rest.
* `translate()` should be used for appropriate positioning of cylinders.
* `rotate()` should be used for the rotation of the fan mount.
* Do not use any non-standard libraries.

I think that covers everything. Let me know if you need clarification.

[Assistant]
FanAdapter.scad:
```
////----FanGrill.scad----
////----OpenSCAD 2015.03-2----
////----2018.08.13----
////----By Kijja Chaovanakij----
////----Revise 2019.01.5.Add fan 35,45 mm. in the fan list.

////----The dapter size must be bigger than the fan size.

/*[Basic] */
//number of fragments[40=low:60=medium:90=high]
$fn=60;
//type of fan adapter
adapter_type=1; //[0:spacer,1:adapter]

/*[Adapter And Fan]*/
//adapter size[4] must greater than fan size
adapter_select=5; //[0:fan25,1:fan30,2:fan35,3:fan40,4:fan45,5:fan50,6:fan60,7:fan70,8:fan80,9:fan92,10:fan120,11:fan140]
//adapter thickness[2]
adapter_thi=2; //[1.6:0.2:3.2]
//The gap between the adapter and the fan, if the item is not up to an automatic size, the script will use the automatic size.
adapter_fan_gap=5; //[3:1:80]

//fan size[3] must be smaller than adapter size
fan_select=3; //[0:fan25,1:fan30,2:fan35,3:fan40,4:fan45,5:fan50,6:fan60,7:fan70,8:fan80,9:fan92,10:fan120,11:fan140]
//fan spacer thickness[2]
fan_thi=2; //[1.6:0.2:40]
//minimun border size[1.0]
min_border=1.0; //[0.8:0.2:2.0]
//degree of fan base rotation to avoid intersection of holes.[15]
fan_rotate=15; //[0:0.5:25]

/*[Grill]*/
//type of grill
grill_type=1; //[0:no grill,1:line,2:circular]
//grill line size[1.8]
grill_size=1.8; //[1.2:0.2:3.2]
//grill line thickness[1.8]
grill_thi=1.8; //[1.2:0.2:3.2]
//grill space factor[3.5]
grill_sp_factor=3.5; //[3.0:0.25:5.0]

/*[Hidden]*/
size=25;
bolt_c2c=20;
hole_dia=2.9;
//do not edit this section
fan25=[[size,25],[bolt_c2c,20],[hole_dia,2.9]];
fan30=[[size,30],[bolt_c2c,24],[hole_dia,3.2]];
fan35=[[size,35],[bolt_c2c,29],[hole_dia,3.2]];
fan40=[[size,40],[bolt_c2c,32],[hole_dia,3.4]];
fan45=[[size,45],[bolt_c2c,37],[hole_dia,4.3]];
fan50=[[size,50],[bolt_c2c,40],[hole_dia,4.3]];
fan60=[[size,60],[bolt_c2c,50],[hole_dia,4.3]];
fan70=[[size,70],[bolt_c2c,61.5],[hole_dia,4.3]];
fan80=[[size,80],[bolt_c2c,72],[hole_dia,4.3]];
fan92=[[size,92],[bolt_c2c,82.5],[hole_dia,4.3]];
fan120=[[size,120],[bolt_c2c,105],[hole_dia,4.3]];
fan140=[[size,140],[bolt_c2c,126],[hole_dia,4.3]];
fan_list=[fan25,fan30,fan35,fan40,fan45,fan50,fan60,
          fan70,fan80,fan92,fan120,fan140];

////----translate var----
adapter_size=fan_list[adapter_select];
asize=lookup(size,adapter_size);
ac2c=lookup(bolt_c2c,adapter_size);
ahole_rad=lookup(hole_dia,adapter_size)/2;
acorner_rad=(asize-ac2c)/2;
ainner_rad=asize/2-min_border;

fan_size=fan_list[fan_select];
fsize=lookup(size,fan_size);
fc2c=lookup(bolt_c2c,fan_size);
fhole_rad=lookup(hole_dia,fan_size)/2;
fcorner_rad=(fsize-fc2c)/2;
finner_rad=fsize/2-min_border;
cone_hi=max(adapter_fan_gap,(asize-fsize)/2);
echo("cone_hi",cone_hi);

////----main----
if (adapter_type==1)
  adapter();
else
  spacer();
////----end main----

module adapter(){
  difference(){
    union(){
//adapter plate
      color("tan")
      translate([0,0,adapter_thi/2])
        frame(asize,ac2c,
          adapter_thi,ainner_rad,
          acorner_rad,true);
//fan plate
      color("yellow")
      translate([0,0,
        adapter_thi+cone_hi+fan_thi/2])
      rotate([0,0,fan_rotate])
        frame(fsize,fc2c,fan_thi,
          finner_rad,fcorner_rad,true);
//fan plate support
      translate([0,0,adapter_thi+cone_hi/2])
      rotate([0,0,fan_rotate])
        frame(fsize,fc2c,cone_hi,
          finner_rad,fcorner_rad,true);
//cone
      translate([0,0,adapter_thi+cone_hi/2])
        cylinder(cone_hi,asize/2,fsize/2,true);
    }//u
//cut inner fan plate support
    translate([0,0,adapter_thi+cone_hi/2])
      cylinder(cone_hi+0.01,ainner_rad,
        finner_rad,true);
//cut adapter bolt hole
    translate([0,0,adapter_thi/2])
      tetra_cylinder(ac2c,ac2c,
        adapter_thi+0.01,ahole_rad,true);
//cut fan bolt hole
    translate([0,0,adapter_thi+cone_hi/2+fan_thi/2])
    rotate([0,0,fan_rotate])
      tetra_cylinder(fc2c,fc2c,
        cone_hi+fan_thi+0.01,fhole_rad,true);
  }//d
}//m
//

module spacer(){
  color("GreenYellow")
  difference(){
    union(){
//fan plate
      translate([0,0,fan_thi/2])
        frame(fsize,fc2c,fan_thi,
          finner_rad,fcorner_rad,true);
//add line grill
      if (grill_type==1)
        translate([0,0,grill_thi/2])
          line_grill();
//add circular grill
      if (grill_type==2)
        translate([0,0,grill_thi/2])
          cicular_grill();
    }//u
//cut fan bolt hole
    translate([0,0,fan_thi/2])
      tetra_cylinder(fc2c,fc2c,
        fan_thi+0.01,fhole_rad,true);
  }//d
}//m
//

module cicular_grill(){
//diagonal line
  for (i=[45,135])
    rotate([0,0,i])
      cube([fsize-min_border,grill_size*1.5,
        grill_thi],true);
//circular line
  for (i=[finner_rad-grill_size*grill_sp_factor:
      -grill_size*grill_sp_factor:0])
    difference(){
      cylinder(grill_thi,i+grill_size/2,
        i+grill_size/2,true);
      if (i > grill_size*4)
        cylinder(grill_thi+0.01,
          i-grill_size/2,i-grill_size/2,true);
    }//d
}//m
//

module line_grill(){
//x axis line
  for (i=[0:grill_size*grill_sp_factor:
      fc2c/2])
    translate([0,i,0])
      cube([fsize-min_border,grill_size,
        grill_thi],true);
  for (i=[0:-grill_size*grill_sp_factor:
      -fc2c/2])
    translate([0,i,0])
      cube([fsize-min_border,grill_size,
        grill_thi],true);
//y axis line
  if (fsize > 80)
    for (i=[-fsize/6,fsize/6])
      translate([i,0,0])
        cube([grill_size*1.5,
          fsize-min_border,grill_thi],true);
  else if (fsize > 40)
    cube([grill_size*1.5,
      fsize-min_border,grill_thi],true);
}//m
//

module frame(size,c2c,height,inner_rad,c_rad,truth){
  difference(){
    hull(){
      for (i=[-c2c/2,c2c/2])
      for (j=[-c2c/2,c2c/2])
        translate([i,j,0])
      cylinder(h=height,r=c_rad,
        r=c_rad,center=truth);
    }//h
    cylinder(height+0.01,inner_rad,inner_rad,truth);
  }//d
}//m
//

module tetra_cylinder(xwid,ywid,height,rad,truth){
  for (i=[-xwid/2,xwid/2])
  for (j=[-ywid/2,ywid/2])
    translate([i,j,0])
    cylinder(h=height,r=rad,r=rad,center=truth);
}//m
//
```


[User]
Hi, I'm looking for help designing a parametric beer bottle/can organizer for my fridge. I need to be able to customize the dimensions for different sized bottles and cans. I want the design to be flexible enough that I can choose between having an open bottom (for easier placement and less material) or a closed bottom (for potentially carrying). I need to be able to adjust the number of bottles/cans in a row and the number of rows. Also, I need mounting towers to connect the different levels of the organizer.

Specifically, I would like you to generate the OpenSCAD code to accomplish this. I will need the following:

1.  **Open Wall Module:** This is a main structural component of the organizer with an open bottom. It should:
    *   Be configurable for the diameter of the bottle/can.
    *   Have a variable number of holes in a row and rows themselves.
    *   Have variable space between the holes and between rows.
    *   Have variable height of the wall.
    *   Have screw holes for mounting at each corner. The size and position of these should be configurable.

2.  **Closed Wall Module:** This is similar to the open wall, but it has a bottom surface. It should:
    *   Be configurable for the diameter of the bottle/can.
    *   Have a variable number of holes in a row and rows themselves.
    *   Have variable space between the holes and between rows.
    *   Have variable height of the wall itself, and the bottom portion.
    *   Have screw holes for mounting at each corner. The size and position of these should be configurable.

3.  **Mounting Tower Module:** This module will connect the open or closed wall modules together. It should:
    *   Have a variable height.
    *   Have a screw hole at its center.
    *   Have a margin around that screw hole for strength.

I need to be able to select which part I want to generate (Open wall, closed wall, or mounting tower) using a variable. For all parts the dimensions and other configuration should be easily modifiable. I would also need the system to calculate the total x and y size based on the number of holes and rows, along with the provided spacing, margins, and diameter.

I'm not too familiar with the different libraries for OpenSCAD, but I think just the standard functionality and perhaps a module for defining a circle might be sufficient.

Could you please generate the OpenSCAD code for these modules?

[Assistant]
Beer_Organizer.scad:
```
// Beer holder scad file by QBA.WTF
// ATTENTION! This is experimental rewrite of my project to the openscad from SolidWorks
// This is NOT TESTED and provided AS IS - use at your own risk!
// NOTE: Screws holes is without threading

// Variables customization

/* [Part Select] */

part = "open_wall"; // [open_wall: Open Wall, closed_wall: Closed Wall, mounting: Mounting tower]

/* [Wall Settings] */

// Beer bottle/can max diameter [mm]
beer_diameter=90;
// Beer bottle/can holes in one row
beer_holes=3;
// Beer bottle/can rows
beer_rows=2;

// Space between each rows [mm]
rows_space=3;
// Space between each beer hole [mm]
beer_space=5;

// Open Wall height [mm]
open_wall_height=5;
// Closed Wall height [mm]
closed_wall_height=7;
// Closed Wall bottom height [mm]
closed_wall_bottom_height=3;

/* [Wall Mounting Settings] */

// Margin between top and bottom line [mm]
top_and_bottom_margin=3;
// Margin between left and right line [mm]
left_and_right_margin=4.43;

// Screw diameter [mm]
screw_diameter=3;
// Space between each screw and corner in x/y [mm]
screw_position=6;

/* [Mounting Tower] */

// [Tower] Height of the mounting tower [mm]
tower_mounting_height=120;

// [Tower] Margin between screw [mm]
tower_screw_margin=3;

// Do not touch - system variables!
x_size = (beer_diameter*beer_holes)+(beer_space*(beer_holes-1))+(left_and_right_margin*2);
y_size = (beer_diameter*beer_rows)+(rows_space*(beer_rows-1))+(top_and_bottom_margin*2);

if(part == "open_wall") {
    open_wall();
} else if(part == "closed_wall") {
    closed_wall();
} else if(part == "mounting") {
    mounting();
}

module open_wall() {
    // Main code
    difference() {
                // Rendering open wall and holes
                 translate([0,0,0])
                    cube([x_size,y_size,open_wall_height],center=false);
                 for(r = [0 : 1 : (beer_rows-1)]) {
                     for(i = [0 : 1 : (beer_holes-1)]) {
                         translate([(beer_diameter/2)+left_and_right_margin+(beer_diameter*i)+(beer_space*i),(beer_diameter/2)+top_and_bottom_margin+(beer_diameter*r)+(rows_space*r),0])
                            linear_extrude(height = open_wall_height, center = false, convexity = 10, twist = 0)
                                circle(d=beer_diameter,$fn=360);
                     }
                }

        // Rendering screws
        translate([screw_position,screw_position,0])
                    linear_extrude(height = open_wall_height+0, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);

        translate([x_size-screw_position,screw_position,0])
                    linear_extrude(height = open_wall_height+0, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);

        translate([screw_position,y_size-screw_position,0])
                    linear_extrude(height = open_wall_height+0, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);

        translate([x_size-screw_position,y_size-screw_position,0])
                    linear_extrude(height = open_wall_height+0, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);
    }
}

module closed_wall() {
    // Main code
    difference() {
        union() {
            difference() {
                // Rendering open wall and holes
                 translate([0,0,closed_wall_bottom_height])
                    cube([x_size,y_size,closed_wall_height],center=false);
                 for(r = [0 : 1 : (beer_rows-1)]) {
                     for(i = [0 : 1 : (beer_holes-1)]) {
                         translate([(beer_diameter/2)+left_and_right_margin+(beer_diameter*i)+(beer_space*i),(beer_diameter/2)+top_and_bottom_margin+(beer_diameter*r)+(rows_space*r),closed_wall_bottom_height])
                            linear_extrude(height = closed_wall_height, center = false, convexity = 10, twist = 0)
                                circle(d=beer_diameter,$fn=360);
                     }
                }
            }
                // Rendering closed wall
                translate([0,0,0])
                    cube([x_size,y_size,closed_wall_bottom_height],center=false);
            }

        // Rendering screws
        translate([screw_position,screw_position,0])
                    linear_extrude(height = open_wall_height+closed_wall_bottom_height, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);

        translate([x_size-screw_position,screw_position,0])
                    linear_extrude(height = open_wall_height+closed_wall_bottom_height, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);

        translate([screw_position,y_size-screw_position,0])
                    linear_extrude(height = open_wall_height+closed_wall_bottom_height, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);

        translate([x_size-screw_position,y_size-screw_position,0])
                    linear_extrude(height = open_wall_height+closed_wall_bottom_height, center = false, convexity = 10, twist = 0)
                        circle(d=screw_diameter,$fn=360);
    }
}

module mounting() {
    difference() {
        translate([0,0,0])
            linear_extrude(height = tower_mounting_height, center = false, convexity = 10, twist = 0)
            circle(d=screw_diameter+tower_screw_margin,$fn=360);

         translate([0,0,0])
            linear_extrude(height = tower_mounting_height, center = false, convexity = 10, twist = 0)
            circle(d=screw_diameter,$fn=360);
    }
}
```


[User]
I want to design a customizable box for trading cards, and I'd like it to be parametric in OpenSCAD. The box should consist of two parts: a main box and a lid.

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

*   `card_x`: The width of the card.
*   `card_y`: The height of the card.
*   `thickness`: The thickness of the box walls.
*   `inner_h`: The inner height of the main box.

The main box should have a rectangular base, with walls that extend upwards, and have a small lip at the top edge. This lip should also be parametric using the `thickness` parameter. The design should also account for stacking, so it should have a slightly recessed base. Additionally, I need small cutouts on the left and right sides of the box at the bottom to help with lid removal.

The lid should fit snugly over the top of the main box. It should also have the lip on it to interlock with the box. The lid should also have a recessed area in the middle, where I'd like to add an image. I'd like the image to be loaded from an external file, so we'll need to define an `image_file` parameter and the `surface()` function should be used to display the image. The image should be inverted. I’ll also need cutouts to match the box cutouts.

I want to be able to easily stack these boxes, so the bottom of the main box should have a small lip that mates with the top edge of another box.

I'd like the design to be modular, so I should be able to call different modules for the main box and for the lid. I'd expect something like a `card_box()` module and a `card_box_cap()` module.

I’d prefer that the design use standard OpenSCAD functions, rather than relying on external libraries like MCAD or BOSL. Can you create the necessary `.scad` files to do this?

[Assistant]
cardbox.scad:
```
// card width
card_x = 70; // [45: 120]

//card height
card_y = 100; // [45: 120]

// wall thickness
thickness = 2.5; // [1.5:0.1:3.5]

// box inner height
inner_h = 40; // [20:100]

image_file = "image.png"; // [image_surface:150x150]

module card_box(card_x, card_y, thickness, inner_height) {
    p1 = [0,0];
    p2 = p1 + [0, inner_height];
    p3 = p2 + [-thickness, 0];
    p4 = p3 + [-thickness, thickness];
    p5 = p4 + [thickness, thickness];
    p6 = p5 + [-thickness, 0];
    p7 = p6 + [-thickness, -thickness];
    p8 = p7 + [0, -thickness];
    p9 = p8 + [2*thickness, -2*thickness];
    p10 = p9 + [0, -inner_height+(3*thickness)];
    p11 = p10 + [-thickness*0.8, -thickness*1.1];
    p12 = p10 + [0, -1.8*thickness];
    p13 = p12 + [thickness+card_x/2,0];
    p14 = p13 + [0, thickness*0.8];

    p=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14];
    difference() {
        union() {
            linear_extrude(height = card_y+2*thickness, center=true) {
                union() {
                    translate([-card_x/2,0,0]) polygon(points=p);
                    mirror([1,0,0]) translate([-card_x/2,0,0]) polygon(points=p);
                }
            }
            translate([0,inner_height/2,-card_y/2-thickness/2]) cube(size=[card_x, inner_height, thickness], center=true);
            translate([0,inner_height/2,card_y/2+thickness/2]) cube(size=[card_x, inner_height, thickness], center=true);
            translate([card_x/2+thickness/2, inner_height-1.25, (-card_y/2)*0.85]) rotate([0,90,0]) cylinder(h=thickness*0.5, r=2, center=true, $fn=10);
            translate([-card_x/2-thickness/2, inner_height-1.25, (-card_y/2)*0.85]) rotate([0,90,0]) cylinder(h=thickness*0.5, r=2, center=true, $fn=10);
        }
        translate([card_x/2+thickness/2, -2.2, (-thickness*1.01)+(-card_y/2)*0.925]) cube([thickness*0.6,2,(card_y/2)*0.15], center=true);
        translate([card_x/2+thickness/2, -2.2, (-card_y/2)*0.85]) cube([thickness,4,5.1], center=true);
        translate([-card_x/2-thickness/2, -2.2, (-thickness*1.01)+(-card_y/2)*0.925]) cube([thickness*0.6,2,(card_y/2)*0.15], center=true);
        translate([-card_x/2-thickness/2, -2.2, (-card_y/2)*0.85]) cube([thickness,4,5.1], center=true);
    }
}

module card_box_cap(card_x, card_y, thickness) {
    p1 = [0,0];
    p2 = p1 + [0,thickness];
    p3 = p2 +  [-thickness, 0];
    p4 = p3 + [-thickness*0.8, -thickness*1.1];
    p5 = p3 + [0, -1.8*thickness];
    p6 = p5 + [thickness+card_x/2,0];
    p7 = p6 + [0, thickness*0.8];

    p = [p1,p2,p3,p4,p5,p6,p7];

    difference() {
        union() {
            linear_extrude(height = card_y+2*thickness, center=true) {
                union() {
                    translate([-card_x/2,0,0]) polygon(points=p);
                    mirror([1,0,0]) translate([-card_x/2,0,0]) polygon(points=p);
                }
            }

            scale([0.4,thickness*0.8,0.4]) rotate([-90,0,0])  surface(file=image_file, center=true, invert=true);
        }
        translate([card_x/2+thickness/2, -2.2, (-thickness*1.01)+(-card_y/2)*0.925]) cube([thickness*0.6,2,(card_y/2)*0.15], center=true);
        translate([card_x/2+thickness/2, -2.2, (-card_y/2)*0.85]) cube([thickness,4,5.1], center=true);
        translate([-card_x/2-thickness/2, -2.2, (-thickness*1.01)+(-card_y/2)*0.925]) cube([thickness*0.6,2,(card_y/2)*0.15], center=true);
        translate([-card_x/2-thickness/2, -2.2, (-card_y/2)*0.85]) cube([thickness,4,5.1], center=true);
    }
}
rotate([90,0,0]) {
    card_box(card_x, card_y, thickness, inner_h);
    translate([card_x +30,0,0]) card_box_cap(card_x, card_y, thickness);
}
```


[User]
I'm looking for an OpenSCAD design for a self-watering planter insert, kind of like the one shown in the code I've attached (SelfwaessernderBlumentopfeinsatz.scad). It needs to be customizable to fit different sized containers.

The basic idea is that it sits inside a larger container, with a water reservoir at the bottom. The plant itself sits in the upper portion, which has slits to allow water to wick up from the reservoir.

Here are the parameters I think it should have:

*   `di`: Inner diameter of the holding container (where the insert will sit). This should have a default value and be adjustable. Let's start with a default of 66mm, and allow it to vary from 40mm to 150mm.
*   `ct`: Thickness of the upper collar (which rests on the top of the holding container) plus the wall thickness. Default to 10mm, ranging from 1mm to 20mm.
*   `ht`: Total height of the insert. Default 90mm, range 30mm to 160mm.
*   `hb`: Height of the outer brim (the part of the collar that extends down). Default 20mm, range 0mm to 30mm.
*   `wt`: Wall thickness of the insert itself. Default to 0.9mm, range 0.5mm to 3mm, with increments of 0.1mm.
*   `am`: Angle of the middle section of the insert. Default to 45 degrees, range 45 to 60 degrees.
*   `it`: Boolean to include or exclude a filler tube. Default to true (1), or can be false (0).
*   `td`: Diameter of the filler tube. Default to 15mm, range 5mm to 20mm. This parameter should only be considered when `it` is true.
*   `ah`: Boolean to include or exclude an adhesion helper (small rim at the base). Default to false (0), can be true (1).
*   `is`: Boolean to include or exclude strengtheners. Default to false (0), can be true (1).
*   `vp`: Boolean to view a profile only. Default false (0), true (1). This isn't essential to the core design but could be a nice debugging aid.

The insert consists of a cylindrical top portion where the plant goes, a conical middle section, and a cylindrical water reservoir area at the bottom, with some slots. There should be a collar on top to hold the insert in place on the container. The fill tube is a simple cylinder that extends into the reservoir, if enabled, and the strengtheners are angled supports around the conical section, if enabled. The adhesion helper is a small flange near the base.

I'd like this to be parametric, so I can easily adjust the size and features. Please generate the complete OpenSCAD file, or set of files, including any helper modules necessary for the design, and any relevant comments.

Also, please don't use any external libraries. I want to keep it simple this time.

[Assistant]
SelbstwaessernderBlumentopfeinsatz.scad:
```
// -----------------------------------------------------
// Selbstwaessernder Blumentopfeinsatz
//
//  Detlev Ahlgrimm, 08.2018
// -----------------------------------------------------

// inner diameter of holding container
di=66;    // [40:150]

// thickness of upper collar (+wallthickness)
ct=10;     // [1:20]

// height total
ht=90;    // [30:160]

// height of outer brim
hb=20;     // [0:30]

// wallthickness
wt=0.9;     // [0.5:0.1:3]

// angle at middle area
am=45;      // [45:60]

// include filler tube
it=1;     // [0:false, 1:true]

// diameter of filler tube
td=15;    // [5:20]

// include adhesion helper
ah=0;     // [0:false, 1:true]

// include strenghtener
is=0;     // [0:false, 1:true]

// view profile only
vp=0;     // [0:false, 1:true]

/*hidden*/
$fn=100;
ho=(di/2-10)*tan(am);     // Hoehe des am-Grad-Bereiches
hu=ht-ho-10;              // Hoehe des eigentlichen Blumentopfes

echo(hu, "+", ho, "+ 10 =", hu+ho+10);

// -----------------------------------------------------
//  d : Durchmesser (aussen)
//  h : Hoehe oder auch Ring-Staerke
// -----------------------------------------------------
module Ring(d, h) {
  translate([0, 0, h/2]) rotate_extrude() translate([d/2-h/2, 0, 0]) circle(d=h);
}

// -----------------------------------------------------
//   r : Radius (innen)
//   w : Wandstaerke
//   h : Hoehe
// -----------------------------------------------------
module Rohr(r, w, h) {
  difference() { cylinder(r=r+w, h=h); translate([0, 0, -0.1]) cylinder(r=r, h=h+0.2); }
}

// -----------------------------------------------------
// ein dreieckiger Ring        (-d/2,0) ---/  (-d/2+s,0)      (d/2-s,0) \--- (d/2,0)
//  d : Durchmesser (aussen)            | /                              \ |
//  s : Seitenlaenge                    |/  (-d/2,-s)           (d/2,-s)  \|
// -----------------------------------------------------
module Ansatz(d, s=1) {
  translate([0, 0, -s]) rotate_extrude() translate([d/2, 0, 0]) polygon([[0, 0], [0, s], [-s, s]]);
}
//!Ansatz(di);

// -----------------------------------------------------
// das massive Teil
// -----------------------------------------------------
module body() {
  union() {
    Ring(di+4-2*wt, 2);                                               // innere Rundung
    Ring(di+2*ct+2*wt, 2);                                            // aeussere Rundung
    Rohr((di+4-2*wt)/2-1, ((di+2*ct+2*wt)-(di+4-2*wt))/2, 2);         // den Raum zwischen den zwei Rundungen schliessen
    translate([0, 0, 1]) Rohr(di/2+ct, wt, hb);                       // die Krempe
    translate([0, 0, 1]) cylinder(d=di, h=hu-1);                      // der eigentliche Blumentopf
    translate([0, 0, hu]) cylinder(d1=di, d2=20, h=ho);               // der am-Grad-Teil
    translate([0, 0, hu+ho]) cylinder(d=20, h=9);                     // der im Wasser haengende Teil
    translate([0, 0, hu+ho+8]) Ring(20, 2);                           // obere Rundung
    translate([0, 0, hu+ho+8]) cylinder(d=18, h=2);                   // obere Rundung schliessen
  }
}
//!body();

// -----------------------------------------------------
// das hohle Teil mit Schlitzen
// -----------------------------------------------------
module theThing() {
  d=wt/sin(am);
  difference() {
    body();
    translate([0, 0, -1]) cylinder(d=di-2*wt, h=hu+1.01);                   // den Blumentopf ausschneiden
    translate([0, 0, hu]) cylinder(d1=di-2*d, d2=20-2*d, h=ho+0.01);        // den am-Grad-Teil ausschneiden
    translate([0, 0, hu+ho-2]) cylinder(d=20-2*wt, h=10);                   // den im Wasser haengenden Teil ausschneiden

    for(a=[0:60:359]) {                                                     // die Schlitze schneiden...
      rotate([0, 0, a])
      translate([9, 0, hu+ho-5]) rotate([0, am, 0]) hull() {                // ...im am-Grad-Teil
        translate([0, 0, 0]) cylinder(d=2, h=10);
        translate([6, 0, 0]) cylinder(d=2, h=10);
      }
    }
    for(a=[0:60:359]) {
      rotate([0, 0, a+30])
      translate([0, 0, hu+ho+2]) rotate([0, 90, 0]) hull() {                // ...und in den im Wasser haengenden Teil
          translate([0, 0, 0]) cylinder(d=2, h=11);
          translate([-5, 0, 0]) cylinder(d=2, h=11);
      }
    }
  }
  translate([0, 0, hu+0.01]) Ansatz(di-2*wt, d-wt);    // Ausgleich der unterschiedlichen Wandstaerken auf der Waagerechten
  if(is) {
    l=sqrt((di/2-10)*(di/2-10)+ho*ho);            // Laenge des am-Grad-Bereiches
    for(a=[0:60:359]) {                           // Verstaerkung dranbappen
      rotate([0, 0, 30+a]) translate([-di/2+wt/2, 0, hu]) rotate([0, 90-am, 0]) union() {
        cylinder(r1=0, r2=wt, h=2);
        translate([0, 0, 2]) cylinder(r=wt, h=l-4);
        translate([0, 0, l-2]) cylinder(r1=wt, r2=0, h=2);
      }
    }
  }
}

// -----------------------------------------------------
// das Teil ... ggf. samt Einfuellstutzen und Brim
// -----------------------------------------------------
module allTogether() {
  difference() {
    theThing();
    if(it) {
      translate([0, 0, 0.1]) intersection() {       // die Oeffnung fuer den Einfuellstutzen ausschneiden
        cylinder(d=di, h=200);
        translate([di/2, 0, hu-0.01]) cylinder(d=td, h=hu+20);
      }
    }
  }
  if(it) {
    intersection() {                                // den Einfuellstutzen zufuegen
      union() {
        body();
        cylinder(d=di, h=2);
      }
      translate([di/2, 0, 0]) Rohr(td/2, wt, hu+ho);
    }
  }
  if(ah==1) {
    cylinder(d=min(di-td-2*wt-1, 20), h=0.3);       // Brim zufuegen
    for(a=[45:90:359]) {
      rotate([0, 0, a]) translate([0, -1, 0]) cube([di/2+1, 2, 0.3]);
    }
  }
}

if(hu>0) {        // die Hoehe des eigentlichen Topfes muss positiv sein
  if(vp==1) {
    difference() {
      allTogether();
      translate([-100, -100, -1]) cube([200, 100, 200]);   // DEBUG: das gesamte Teil halbieren
    }
  } else {
    rotate([0, 0, -120]) allTogether();   // bei 120 Grad Drehung erzeugt Cura kuerzere Retraction-Wege
  }
} else {
  text("illegal ration between 'di' and 'ht'!");
}

```


[User]
I'm looking to generate a set of OpenSCAD files for creating square tubing connectors. I've got some existing code for reference, but I want something a bit more robust and easier to customize.

Essentially, I need connectors for different configurations of square tubing:
*   **Fit Check:** A test piece to dial in the correct inner dimension for a snug fit on the tubing. This should have a series of slightly different inner diameters.
*   **Plug:** A simple plug to cap the end of a tube.
*   **L-Connector:** A 90-degree corner connector.
*   **Union:** A straight connector to join two tubes end-to-end.
*   **T-Connector:** A connector with a 'T' shape, joining three tubes.
*   **Corner:** A cube with three protrusions, like an "L" shaped edge of a cube.
*   **Cross:** A four-way connector, where four tubes meet at a central point
*   **Mid-Edge:** A connector like the "corner," but only with protrusions on the edge of a face.
*   **Mid-Face:** A connector with the protrusions coming out of the face, like a "T" with no through-hole.
*   **Interior:** Like the "Mid-Face" but with another opposite protrusion.

The key parameters I want to be able to adjust are:
*   **`insideDim`**: The inner dimension of the protrusion, which should fit snugly over the square tubing.
*   **`outsideDim`**: The outer dimension of the main cube part of the connector.
*   **`protrusionLength`**: The length of the protrusions.
*   **`roundoffRadius`**:  The radius of the rounded edges on the protrusions.
*   **`fittingType`**: An integer from 0 to 9, used to select which of the different connector types to generate.

I'd like to have a separate module for the 'protrusion' shape, which I think uses Minkowski sum to create the rounded corners on a rectangular prism. I'd also like the 'Fit Check' part to generate a set of five protrusions with varying inside dimensions, and display the actual dimensions of each.  For the text, I'd like to use the `text()` module in OpenSCAD, instead of relying on the `numtex` module in the reference code. I think that should work fine with the default text options.

Could you generate OpenSCAD files that would produce these connectors? I am not using any external libraries.

[Assistant]
square_tubing_connector_multi.scad:
```
// Square tubing corner connector
// David O'Connor  david@lizardstation.com
// 14 August 2018
// 13 February 2020  Fixed minor typo

// Copyright Creative Commons Attribution Unported 3.0 (CC-BY-3.0)
//   (https://creativecommons.org/licenses/by/3.0/)

// Inside dimension
insideDim = 9.4; // [6:0.1:30]
// Outside dimension
outsideDim = 12.7;// [6:0.1:30]
// Protrusion length
protrusionLength = 15;// [6:0.2:60]
// Roundoff radius
roundoffRadius = 1;// [1.0:0.1:4.0]
// Connector type
fittingType = 0;// [0:Fit_Check, 1:Plug, 2:L, 3:Union, 4:T, 5:Corner, 6:Cross, 7:Mid_Edge, 8:Mid_Face, 9:Interior]

bracket(id=insideDim, od=outsideDim, l=protrusionLength, r=roundoffRadius, type=fittingType, $fa=5, $fs=0.5);

module bracket(id=6, od=8, l=8, r=1, type=3) {
    m = l/2+od/2;
    textScaling = od*0.032;

    // This part provides five protrusions in increments of 0.1 mm to help you find the optimum size
    if (type == 0) {
        union() {
            dw = 0.1;
            cube([od, 5*od, od], center=true);
            for (i = [-2:2]) {
                translate([m, od*i, 0]) {
                    rotate([0, 90, 0]) {
                        protrusion(id-dw*i, l, r);
                    }
                    translate([-od*1.1, 0, od/2])
                        scale([textScaling, textScaling, 1.5*textScaling])
                        if (i == 0)
                            numtex(id, 1, false);
                        else
                            numtex(dw*i, 1, true);
                }
            }
        }
    }
    else {
    union() {

        // The Center Cube
        cube([od, od, od], center=true);

        // All the parts have at lease one protrusion
        translate([0, 0, m])
            protrusion(id, l, r);

        // The rest depend on the type selected
        if ((type==3) || (type==4) || (type==6) || (type==7) || (type==8) || (type==9))
            translate([0, 0, -m])
                protrusion(id, l, r);

        if ((type==2) || (type==4) || (type==5) || (type==6) || (type==7) || (type==8) || (type==9))
            translate([m, 0, 0])
                rotate([0, 90, 0])
                    protrusion(id, l, r);

        if ((type==6) || (type==8) || (type==9))
            translate([-m, 0, 0])
                rotate([0, 90, 0])
                    protrusion(id, l, r);

        if ((type==5) || (type==7) || (type==8) || (type==9))
            translate([0, m, 0])
                rotate([90, 0, 0])
                    protrusion(id, l, r);

        if(type==9)
            translate([0, -m, 0])
                rotate([90, 0, 0])
                    protrusion(id, l, r);
    }
}
}

// Create the protrusion, with rounded corners
// t = thickness, l = length, r = radius
module protrusion(t, l, r) {
    minkowski() {
    cube([t-2*r, t-2*r, l], center=true);
        sphere(r=r);
    }
}

// Convert a floating point number into a 2D text element
// x:   The floating point number
// dec: Number of digits past the decimal point to print
// plus:  true to show the + for positive numbers
module numtex(x = 3.14159, dec = 1, plus=false) {
    rounded = round(x * pow(10, dec)) * pow(10, -dec);
    //print_string = str(rounded);
    //linear_extrude(1)
    //text(print_string, font = "Liberation Sans:style=Bold Italic", halign="center", valign="center");
    linear_extrude(1)
        if ((x <=0) || (plus==false)) {
            text(str(rounded), font = "Liberation Sans:style=Bold Italic", halign="center", valign="center");
        } else {
            text(str("+", rounded), font = "Liberation Sans:style=Bold Italic", halign="center", valign="center");
        }

}

```


[User]
Hi, I'm looking to generate a parametric breadboard in OpenSCAD, and I'd like some help with the code. I've seen some examples, but I need something that I can customize a lot. I want to be able to change the number of rows and columns, the pin spacing and diameter, and even how the pins are grouped together.

Specifically, I need to define these parameters:

*   `rows`: Number of pin rows.
*   `columns`: Number of pin columns.
*   `pinSpacing`: Distance between pin centers, preferably in millimeters (default: 2.54mm).
*   `pinDiameter`: Diameter of a pin hole/section, also in millimeters.
*   `pinsPerGroup`: Number of pins in a horizontal group.
*   `pinGroupSpacing`: Space between horizontal pin groups, in number of pins.
*   `pinBevelEnabled`: Boolean to enable or disable a bevel on the top of the pin holes.
*   `pinBevelAmount`:  The amount of bevel (in mm), if enabled.
*   `pinBevelSteepness`: The steepness of the bevel.

I also need to include optional power terminals on the sides:
*   `powerLeftEnabled`: Boolean to enable or disable the left power terminal.
*   `powerRightEnabled`: Boolean to enable or disable the right power terminal.
*   `pinsPerPowerGroup`: Number of pins in a vertical power terminal group.
*   `pinPowerGroupSpacing`: Space between vertical power terminal pin groups (in pins).
*   `powerGroupPinOffsetTop`: Offset of the first power terminal group from the top, in number of pins.
*   `powerGroupPinOffsetBottom`: Offset of the last power terminal group from the bottom, in number of pins.

Then some overall breadboard parameters:
*   `horizontalBorderWidth`: Width of the horizontal border (in pins).
*   `verticalBorderWidth`: Width of the vertical border (in pins).
*   `boardHeight`: Height of the breadboard, in mm.

Finally, for wiring, I would like:
*   `wiringSectionEnabled`: Boolean to enable or disable wiring sections on the bottom.
*   `wiringSectionTollerance`: Additional cut out space around wiring sections in mm.
*   `wallThickness`: The width of the walls between wiring sections, in mm.
*   `faceThickness`: Thickness of the top face of the breadboard, in mm.

I'm not sure about libraries, I guess basic OpenSCAD functions are enough? It would be great if you could generate the OpenSCAD code for me. I don't need any fancy features, just a way to produce the shapes correctly and customize their size.

[Assistant]
breadboard.scad:
```

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

/*[Pin Configuration]*/

// Number of pin rows
rows = 63;  // [1:1:100]

// Number of pin columns
columns = 10;   // [1:1:40]

// Side length of one pin section [in mm]
pinDiameter = 1.0;  // [0.4:0.1:4.0]

// Distance from pin-center to pin-center [in mm] (default: 0.l inch = 2.54 mm)
pinSpacing = 2.54;  // [2.54:0.1 Inch, 2.0:2.0 mm]

// Pins per horizontal pin-group
pinsPerGroup = 5;   // [1:1:40]

// Space between horizontal pin groups [in pins]
pinGroupSpacing = 3;    // [1:0.5:5]

// Add or remove bevel on upper side of the breadboard
pinBevelEnabled = true; // [true:Enabled, false:Disabled]

// Amount of Pin-bevel on upper side of the breadboard [in mm]
pinBevelAmount = 0.3;   // [0.0:0.1:1.0]

// Steepness of Pin-bevel on upper side of the breadboard
pinBevelSteepness = 1;  // [0.0:0.1:3.0]

/*[Power-Terminal Configuration]*/

// Add left power-terminal
powerLeftEnabled = true;    // [true:Enabled, false:Disabled]

// Add right power-terminal
powerRightEnabled = true;   // [true:Enabled, false:Disabled]

// Pins per vertical power-terminal pin group
pinsPerPowerGroup = 5;  // [1:1:40]

// Space between vertical power-terminal pin groups [in pins]
pinPowerGroupSpacing = 2;   // [1:0.5:5]

// Offset of the first power-terminal pin group [in pins]
powerGroupPinOffsetTop = 2; // [0.0:0.5:5.0]

// Offset of the last power-terminal pin group [in pins]
powerGroupPinOffsetBottom = 2;  // [0.0:0.5:5.0]

/*[Board Configuration]*/

// Horizontal border-width [in pins]
horizontalBorderWidth = 1.5;    // [0.5:0.5:5.0]

// Vertical border-width [in pins]
verticalBorderWidth = 1.5;  // [0.5:0.5:5.0]

// Height of the breadboard [in mm]
boardHeight = 9.5;  // [5.0:0.5:15.0]

/*[Wiring-Section Configuration]*/

// Enable or disable wiring sections on the bottom of the breadboard
wiringSectionEnabled = true;    // [true:Enabled, false:Disabled]

// Additional cutted out space for the first and last pin of each wiring section, to make wiring easier and fit different types of wirings [in mm]
wiringSectionTollerance = 0;    // [0:0.1:10.0]

// Width of the walls separating the wiring sections, seen from below [in mm]
wallThickness = 1.2;    // [0.1:0.1:3.0]

// Thickness of the face of the board where the building-parts are placed [in mm]
faceThickness = 1;  // [0.4:0.1:2.0]

breadboard(
    rows = rows,
    columns = columns,
    pinSpacing = pinSpacing,
    pinDiameter = pinDiameter,
    pinsPerGroup = pinsPerGroup,
    pinGroupSpacing = pinGroupSpacing,
    pinBevelEnabled = pinBevelEnabled,
    pinBevelAmount = pinBevelAmount,
    pinBevelSteepness = pinBevelSteepness,
    powerLeftEnabled = powerLeftEnabled,
    powerRightEnabled = powerRightEnabled,
    pinsPerPowerGroup = pinsPerPowerGroup,
    pinPowerGroupSpacing = pinPowerGroupSpacing,
    powerGroupPinOffsetTop = powerGroupPinOffsetTop,
    powerGroupPinOffsetBottom = powerGroupPinOffsetBottom,
    horizontalBorderWidth = horizontalBorderWidth,
    verticalBorderWidth = verticalBorderWidth,
    boardHeight = boardHeight,
    wiringSectionEnabled = wiringSectionEnabled,
    wiringSectionTollerance = wiringSectionTollerance,
    wallThickness = wallThickness,
    faceThickness = faceThickness
);

// Examples:

// standard 830 pin breadboard
//breadboard();

// standard 400 pin breadboard
/*
breadboard(
    rows=30,
    powerGroupPinOffsetTop=0.5,
    powerGroupPinOffsetBottom=0.5
);
*/

// small custom breadboard
/*
breadboard(
    rows=17,
    powerLeftEnabled=false,
    powerRightEnabled=false
);
*/

// highly customized breadboard
/*
breadboard(
    rows=10,
    columns=20,
    powerLeftEnabled=true,
    powerRightEnabled=false,
    pinSpacing=2.54,
    pinsPerGroup=10,
    pinGroupSpacing=2,
    pinsPerPowerGroup=1,
    pinPowerGroupSpacing=1,
    powerGroupPinOffsetTop=0,
    powerGroupPinOffsetBottom=0,
    horizontalBorderWidth=1,
    verticalBorderWidth=1,
    boardHeight=5,
    pinDiameter=1.6,
    wallThickness=1.6,
    faceThickness=2
);
*/

/*
rows: Number of pin rows
columns: Number of pin columns
pinSpacing: Distance from pin-center to pin-center [in mm] (default: 0.l inch = 2.54 mm)
pinDiameter: Side length of one pin section [in mm]
pinsPerGroup: Pins per horizontal pin-group
pinGroupSpacing: Space between horizontal pin groups [in pins]
pinBevelEnabled: Add or remove bevel on upper side of the breadboard
pinBevelAmount: Amount of Pin-bevel on upper side of the breadboard [in mm]
pinBevelSteepness: Steepness of Pin-bevel on upper side of the breadboard
powerLeftEnabled: Add left power-terminal
powerRightEnabled: Add right power-terminal
pinsPerPowerGroup: Pins per vertical power-terminal pin group
pinPowerGroupSpacing: Space between vertical power-terminal pin groups [in pins]
powerGroupPinOffsetTop: Offset of the first power-terminal pin group [in pins]
powerGroupPinOffsetBottom: Offset of the last power-terminal pin group [in pins]
horizontalBorderWidth: Horizontal border-width [in pins]
verticalBorderWidth: Vertical border-width [in pins]
boardHeight: Height of the breadboard [in mm]
wiringSectionEnabled: Enable or disable wiring sections on the bottom of the breadboard
wiringSectionTollerance: Additional cutted out space for the first and last pin of each wiring section, to make wiring easier and fit different types of wirings [in mm]
wallThickness: Width of the walls separating the wiring sections, seen from below [in mm]
faceThickness: Thickness of the face of the board where the building-parts are placed [in mm]
*/
module breadboard(
    rows = 63,
    columns = 10,
    pinSpacing = 2.54,
    pinDiameter = 1.0,
    pinsPerGroup = 5,
    pinGroupSpacing = 3,
    pinBevelEnabled = true,
    pinBevelAmount = 0.3,
    pinBevelSteepness = 1,
    powerLeftEnabled = true,
    powerRightEnabled = true,
    pinsPerPowerGroup = 5,
    pinPowerGroupSpacing = 2,
    powerGroupPinOffsetTop = 2,
    powerGroupPinOffsetBottom = 2,
    horizontalBorderWidth = 1.5,
    verticalBorderWidth = 1.5,
    boardHeight = 9.5,
    wiringSectionEnabled = true,
    wiringSectionTollerance = 0,
    wallThickness = 1.2,
    faceThickness = 1
) {

    // Calc board-size
    // width:
    boardWidth = ((columns - 1) * pinSpacing)           // space for pins
        + ((horizontalBorderWidth * 2) * pinSpacing)    // space for border
        + (floor((columns - 1) / pinsPerGroup) * (pinGroupSpacing-1) * pinSpacing);// space for pin-group-spacing

    // length:
    boardLength = ((rows - 1) * pinSpacing)         // space for pins
        + ((verticalBorderWidth * 2) * pinSpacing); // space for border

    union() {

        // Main breadboard
        difference() {

            // breadboard base body
            cube([boardWidth, boardLength, boardHeight]);

            // insert pins
            for(pinRow = [0:rows-1]) {

                for(pinColumn = [0:columns-1]) {

                    // Calc pins x-position
                    posX = (horizontalBorderWidth * pinSpacing) // space for border
                        + (pinColumn * pinSpacing)              // space for pins
                        + (floor(pinColumn / pinsPerGroup) * (pinGroupSpacing-1) * pinSpacing);// space for pin groups

                    // Calc pins y-position
                    posY = (verticalBorderWidth * pinSpacing) // space for border
                        + (pinRow * pinSpacing);                // space for pins

                    posZ = -0.05 - (0.05*(pinBevelEnabled == true ? 1 : 0));

                    translate([posX, posY, posZ]) {
                        pinSection(pinDiameter=pinDiameter, height=boardHeight+0.1, bevelEnabled=pinBevelEnabled, bevelAmount=pinBevelAmount, bevelSteepness=pinBevelSteepness);
                    }

                }

            }

            if(wiringSectionEnabled == true) {

                // cut out space for "wiring"
                for(wiringRow = [0:rows-1]) {

                    for(wiringColumn = [0:floor((columns-1) / pinsPerGroup)]) {

                        // Calc wiringSection x-position
                        posX = (horizontalBorderWidth * pinSpacing) // space for border
                            + (((pinsPerGroup-1) / 2) * pinSpacing) // center on first pin group
                            + (wiringColumn * (pinGroupSpacing-1) * pinSpacing)  // space for pin groups
                            + (wiringColumn * pinsPerGroup * pinSpacing);   // adapt to current column

                        // Calc wiringSection y-position
                        posY = (verticalBorderWidth * pinSpacing)   // space for border
                            + (wiringRow * pinSpacing);             // adapt to current row

                        translate([posX, posY, -1])
                            wiringSection(
                                width = (pinSpacing * pinsPerGroup) - wallThickness + (wiringSectionTollerance * 2),
                                length = pinSpacing - wallThickness,
                                height = boardHeight - faceThickness + 1
                            );
                    }

                }

            }

        }

        // left power-terminal
        if(powerLeftEnabled == true) {

            // Calc sizes, etc.
            powerLeftBoardWidth = pinSpacing    // space for pins
                + ((horizontalBorderWidth * 2) * pinSpacing); // space for border

            powerLeftBoardLength = boardLength;

            // move to the left of the breadboard
            translate([-powerLeftBoardWidth, 0, 0]) {

                difference() {

                    // left power-terminal base body
                    cube([powerLeftBoardWidth, powerLeftBoardLength, boardHeight]);

                    numPinSpacings = ceil(
                        (
                            (rows - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) // num relevant pins
                            + (pinPowerGroupSpacing-1)  // add power group pin spacing
                        ) / (
                            pinsPerPowerGroup + (pinPowerGroupSpacing - 1)  // divide by size of powerGroup + powerGroupSpacing
                        )
                    ) - 1;

                    // Calculate number of power-pin-rows
                    powerPinRows = rows // start with number of rows
                        - powerGroupPinOffsetTop    // remove top offset
                        - powerGroupPinOffsetBottom // remove bottom offset
                        - (numPinSpacings * (pinPowerGroupSpacing-1));   // remove group-spacings

                    if(powerPinRows >= 1) {

                        // insert pins
                        for(pinRow = [0:powerPinRows-1]) {

                            for(pinColumn = [0:1]) {

                                // Calc pins x-position
                                posX = (horizontalBorderWidth * pinSpacing) // space for border
                                    + (pinColumn * pinSpacing);             // space for pins

                                // Calc pins y-position
                                posY = (verticalBorderWidth * pinSpacing) // space for border
                                    + (pinRow * pinSpacing)                 // space for pins
                                    + (powerGroupPinOffsetTop * pinSpacing) // top offset
                                    + (floor(pinRow / pinsPerPowerGroup) * (pinPowerGroupSpacing-1) * pinSpacing);// space for pin groups

                                posZ = -0.05 - (0.05*(pinBevelEnabled == true ? 1 : 0));

                                translate([posX, posY, posZ]) {
                                    pinSection(pinDiameter=pinDiameter, height=boardHeight+0.1, bevelEnabled=pinBevelEnabled, , bevelAmount=pinBevelAmount, bevelSteepness=pinBevelSteepness);
                                }

                            }

                        }

                    }

                    if(wiringSectionEnabled == true) {

                        // cut out space for "wiring"
                        posX = horizontalBorderWidth * pinSpacing;
                        posY = (rows - 1 - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) / 2 * pinSpacing + ((verticalBorderWidth + powerGroupPinOffsetTop) * pinSpacing);

                        translate([posX, posY, -1])
                            wiringSection(
                                width = pinSpacing - wallThickness,
                                length = ((rows - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) * pinSpacing) - wallThickness + (wiringSectionTollerance * 2),
                                height = boardHeight - faceThickness + 1
                            );

                        translate([posX + pinSpacing, posY, -1])
                            wiringSection(
                                width = pinSpacing - wallThickness,
                                length = ((rows - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) * pinSpacing) - wallThickness + (wiringSectionTollerance * 2),
                                height = boardHeight - faceThickness + 1
                            );

                    }

                }

            }

        }

        // right power-terminal
        if(powerRightEnabled == true) {

            // Calc sizes, etc.
            powerRightBoardWidth = pinSpacing    // space for pins
                + ((horizontalBorderWidth * 2) * pinSpacing); // space for border

            powerRightBoardLength = boardLength;

            // move to the right of the breadboard
            translate([boardWidth, 0, 0]) {

                difference() {

                    // right power-terminal base body
                    cube([powerRightBoardWidth, powerRightBoardLength, boardHeight]);

                    numPinSpacings = ceil(
                        (
                            (rows - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) // num relevant pins
                            + (pinPowerGroupSpacing-1)  // add power group pin spacing
                        ) / (
                            pinsPerPowerGroup + (pinPowerGroupSpacing - 1)  // divide by size of powerGroup + powerGroupSpacing
                        )
                    ) - 1;

                    // Calculate number of power-pin-rows
                    powerPinRows = rows // start with number of rows
                        - powerGroupPinOffsetTop    // remove top offset
                        - powerGroupPinOffsetBottom // remove bottom offset
                        - (numPinSpacings * (pinPowerGroupSpacing-1));   // remove group-spacings

                    if(powerPinRows >= 1) {

                        // insert pins
                        for(pinRow = [0:powerPinRows-1]) {

                            for(pinColumn = [0:1]) {

                                // Calc pins x-position
                                posX = (horizontalBorderWidth * pinSpacing) // space for border
                                    + (pinColumn * pinSpacing);             // space for pins

                                // Calc pins y-position
                                posY = (verticalBorderWidth * pinSpacing) // space for border
                                    + (pinRow * pinSpacing)                 // space for pins
                                    + (powerGroupPinOffsetTop * pinSpacing) // top offset
                                    + (floor(pinRow / pinsPerPowerGroup) * (pinPowerGroupSpacing-1) * pinSpacing);// space for pin groups

                                posZ = -0.05 - (0.05*(pinBevelEnabled == true ? 1 : 0));

                                translate([posX, posY, posZ]) {
                                    pinSection(pinDiameter=pinDiameter, height=boardHeight+0.1, bevelEnabled=pinBevelEnabled, , bevelAmount=pinBevelAmount, bevelSteepness=pinBevelSteepness);
                                }

                            }

                        }

                    }

                    if(wiringSectionEnabled == true) {

                        // cut out space for "wiring"
                        posX = horizontalBorderWidth * pinSpacing;
                        posY = (rows - 1 - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) / 2 * pinSpacing + ((verticalBorderWidth + powerGroupPinOffsetTop) * pinSpacing);

                        translate([posX, posY, -1])
                            wiringSection(
                                width = pinSpacing - wallThickness,
                                length = ((rows - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) * pinSpacing) - wallThickness + (wiringSectionTollerance * 2),
                                height = boardHeight - faceThickness + 1
                            );

                        translate([posX + pinSpacing, posY, -1])
                            wiringSection(
                                width = pinSpacing - wallThickness,
                                length = ((rows - powerGroupPinOffsetTop - powerGroupPinOffsetBottom) * pinSpacing) - wallThickness + (wiringSectionTollerance * 2),
                                height = boardHeight - faceThickness + 1
                            );

                    }

                }

            }

        }

    }

}

module pinSection(pinDiameter=0.8, height=10, bevelEnabled = true, bevelAmount=0.3, bevelSteepness=1) {

    translate([-pinDiameter / 2, -pinDiameter / 2, 0])
        cube([pinDiameter, pinDiameter, height]);

    if(bevelEnabled == true) {
        translate([0, 0, height+0.001])
            rotate([180, 0, 0])
                polyhedron(
                  points=[
                    // the four points at base
                    [(-(pinDiameter+bevelAmount) / 2),(-(pinDiameter+bevelAmount) / 2),0],
                    [(-(pinDiameter+bevelAmount) / 2),((pinDiameter+bevelAmount) / 2),0],
                    [((pinDiameter+bevelAmount) / 2),((pinDiameter+bevelAmount) / 2),0],
                    [((pinDiameter+bevelAmount) / 2),(-(pinDiameter+bevelAmount) / 2),0],
                    // the apex point
                    [0,0,((pinDiameter+bevelAmount) / 2)*bevelSteepness]
                  ],
                  faces=[ [0,1,4],[1,2,4],[2,3,4],[3,0,4],              // each triangle side
                              [1,0,3],[2,1,3] ]                         // two triangles for square base
                 );
    }

}

module wiringSection(width=6.7, length=1.34, height=8) {

    translate([-width / 2, -length / 2, 0])
        cube([width, length, height]);

}

```


[User]
I want to design a parametric cupcake wrapper planter and saucer set in OpenSCAD. It should look like a typical pleated paper cupcake wrapper, but be solid and suitable for 3D printing.

Here's what I need:

1.  **Cupcake Wrapper (Planter):**
    *   It should have a star-shaped profile when viewed from the top, mimicking the pleats of a cupcake liner. The number of "pleats" (star points) should be adjustable. I'd like to be able to specify the outer radius of the top and the inner radius of those top points.
    *   The base should be narrower than the top, also star-shaped, and smoothly transition to the top profile. I need to control the base width as a percentage of the top width.
    *   The height of the planter should be adjustable.
    *   The walls should be of adjustable thickness and I want to make sure the inner and outer profiles stay consistent for the whole height.
    *   I need an option to add a drain hole in the center of the base with an adjustable diameter.

2.  **Saucer:**
    *   The saucer should have a similar star-shaped profile to the planter, and it should have its own adjustable width and height. The outer shape of the saucer should be bigger than the planter's base, to catch any water drainage.
    *   It also needs adjustable wall thickness, maintaining consistent inner and outer profiles throughout the height.

3.  **General Requirements:**
    *   All dimensions (width, height, radius, wall thickness, etc.) should be parametric and adjustable via variables at the top of the OpenSCAD file.
    *   I want to be able to scale the entire design by a single scaler variable, keeping proportions consistent.
    *   I need an option to render only the planter, only the saucer, or both.
    *   I want to use the built-in OpenSCAD functions, but I don't need any external libraries.

Can you generate the OpenSCAD code for this?

[Assistant]
simple-cupcake.scad:
```
// cupcake wrapper planter-pot
// 2018-08-16 Lucina
// points = number of points (minimum 3)
// outer  = radius to outer points
// inner  = radius to inner points
// preview[view:south east, tilt:top diagonal]
/* [cupcake wrapper settings] */
// scaler to resize keeping the same proportions
scaler = 1.0 ;   //[.25:.25:4]
// wall thickness (mm)
wall = .9; // [.6:.1:10]
// cut out drain hole
drainHole = "yes";   // [yes, no]
// which model to render: planter, saucer or both
selection = "planter";   // [planter, saucer]

/* [hidden] */
$fn = 60;
x = 0; y = 1; z = 2;
// planter width (mm)
planterWidth = 70*scaler;
// planter height (mm)
planterHeight = 35*scaler;
// base width as percentage of top width
baseWidthPercent = 71;
// saucer height
sauHeight = 12*scaler;
// drain hole (mm)
hole = 8*scaler;
// number of sides
numberOfSides = 32;
allow = 4;
baseWidth = (baseWidthPercent/100)*planterWidth;
inner = .9;
layer = .1;
nLayers = round(planterHeight/layer);
zStep = layer;
xStep = (planterWidth-baseWidth)/nLayers;
innerbaseWidth = baseWidth-2*wall;
sauLayers = sauHeight/layer;

module rstar(xx, zz) {
   linear_extrude(height=zz, convexity=10, twist=0)
      Star(numberOfSides, xx*.5, inner*xx*.5);
}

module planter() {
   difference() {
      for ( i = [0:nLayers] ) {
         translate([0, 0, i*zStep])
         rstar(baseWidth+i*xStep, layer);
      }
      for ( i = [0:nLayers] ) {
         translate([0, 0, i*zStep+wall])
         rstar(innerbaseWidth+i*xStep, layer);
      }
   }
}
module saucer() {
   difference() {
      for ( i = [0:sauLayers] ) {
         translate([0, 0, i*zStep])
         rstar(baseWidth+allow+i*xStep, layer);
      }
      for ( i = [0:sauLayers] ) {
         translate([0, 0, i*zStep+wall])
         rstar(innerbaseWidth+allow+i*xStep, layer);
      }
   }
}
module Star(points, outer, inner) {

	// polar to cartesian: radius/angle to x/y
	function x(r, a) = r * cos(a);
	function y(r, a) = r * sin(a);

	// angular width of each pie slice of the star
	increment = 360/points;

	union() {
		for (p = [0 : points-1]) {

			// outer is outer point p
			// inner is inner point following p
			// next is next outer point following p

			x_outer = x(outer, increment * p);
         y_outer = y(outer, increment * p);
         x_inner = x(inner, (increment * p) + (increment/2));
         y_inner = y(inner, (increment * p) + (increment/2));
         x_next  = x(outer, increment * (p+1));
         y_next  = y(outer, increment * (p+1));
				polygon(points = [[x_outer, y_outer], [x_inner, y_inner], [x_next, y_next], [0, 0]], paths  = [[0, 1, 2, 3]]);
			}
   }
}
if ( selection == "saucer" ) {
   saucer();
}
if ( selection == "planter" ) {
   difference() {
      planter();
      if ( drainHole == "yes" ) {
         translate([0, 0, -wall])
            cylinder(h=2*wall, d=hole);
      }
   }
}
```


[User]
Okay, I'm looking for an OpenSCAD design for a two-part fan adapter, similar to the one I found on Thingiverse (link provided in the description). I need it to be parametric, allowing for different fan and vent sizes, and I want to be able to print it without needing supports. The main goal is to adapt a smaller fan to a larger vent or vice-versa, while making it easy to tighten screws.

Here's a breakdown of the features I need:

**General Structure:**

*   **Two-Part Design:** The adapter should consist of two parts that fit together snugly without needing screws – think of a friction fit with overlapping ducts.
*   **Easy Screw Tightening:** The design should allow for easy access to screws for both the fan and the vent side.
*   **Versatile Sizing:** It needs to accommodate a range of fan sizes and vent sizes. Ideally, it should have pre-set options for common fan sizes from 25mm to 140mm.
*   **Print Test:** I'd like an option to generate test prints to verify the fit of the inner and outer ducts before printing the full adapter.
*   **Support-Free Printing:** The cone connecting the two parts should have a maximum slope of 45 degrees to ensure it prints without supports.
*  **Adjustable Offset:** The design needs to let me adjust the offset distance between the fan and vent edges so they remain in roughly the same position when switching between different sizes of fan and adapter.
*   **Fan Grill Options:** I need the ability to include or exclude fan guards (grills) on both the fan and vent sides. These grills should have options for either circular or line designs.

**Specific Parameters:**

*   **Fan Size:** Predefined list of fan sizes (25mm, 30mm, 40mm, 50mm, 60mm, 70mm, 80mm, 92mm, 120mm, 140mm).
*   **Vent Size:** Similar to fan size, a predefined list of sizes (25mm, 30mm, 40mm, 50mm, 60mm, 70mm, 80mm, 92mm, 120mm, 140mm).
*   **Plate Thickness:** Separate thickness values for both the fan and vent side plates.
*   **Grill Type:** Options for "no grill", "circular grill", and "line grill" for both fan and vent sides.
*   **Grill Line Size:** Thickness of the grill lines.
*   **Grill Line Thickness:** Thickness of the grill elements.
*   **Grill Spacing Factor:**  Controls the space between grill lines/circles.
*   **Minimum Border Size:** The minimum wall thickness for the fan and adapter plates.
*   **Duct Height:** The height of the overlapping ducts.
*   **Duct Tolerance:** The clearance between the inner and outer ducts for a good fit.
*   **Adapter-Fan Gap:** The distance between the adapter and the fan mounting surfaces.
*   **Gap X Offset:**  Offset distance in the X direction.
*   **Gap Y Offset:**  Offset distance in the Y direction.

**Output:**

I'd prefer if you could generate the OpenSCAD files to fully implement this adapter. I am assuming there is no need to use any external libraries.

Could you please provide the necessary .scad file(s) based on these requirements? Let me know if you need more information.

[Assistant]
2PartFanAdapter.scad:
```
//// File: 2PartFanAdapter.scad
//// Version: OpenSCAD 2015.03-2
//// Author: Kijja Chaovanakij
//// Date: 2018-08-21
////
////The dapter size must be bigger than the fan size.

/*[var]*/
//smooth
$fn=36; //[36=low, 48=medium, 60=high]
//generate parts.[The print test is find the fit of inner duct and outer duct.]
part=4; //[1:print test, 2:fan frame, 3:adapter frame, 4:both frame]

/*[Adapter]*/
//adapter size must greater than fan size
adapter_select=8; //[0:fan25, 1:fan30, 2:fan40, 3:fan50, 4:fan60, 5:fan70, 6:fan80, 7:fan92, 8:fan120, 9:fan140]
//adapter plate thickness[2]
adapter_thi=2; //[1.6:0.2:3.2]
//adapter grill type
adapter_grill_type=0; //[0:no grill, 1:circular, 2:line]

/*[Fan]*/
//fan size must be smaller than adapter size
fan_select=2; //[0:fan25, 1:fan30, 2:fan40, 3:fan50, 4:fan60, 5:fan70, 6:fan80, 7:fan92, 8:fan120, 9:fan140]
//fan plate thickness[2]
fan_thi=2; //[1.6:0.2:40]
//fan grill type
fan_grill_type=0; //[0:no grill, 1:circular, 2:line]

/*[Grill]*/
//grill line size[1.8]
grill_size=1.8; //[1.2:0.2:3.2]
//grill line thickness[1.8]
grill_thi=1.8; //[1.2:0.2:3.2]
//grill space factor[3.5]
grill_sp_factor=3.5; //[3.0:0.25:5.0]

/*[Border, Duct and Gap]*/
//minimum border size of fan plate and adapter plate or duct wall thickness
min_border=1; //[1:0.2:2]
//overlap duct height
duct_hi=8; //[8:1:15]
//fit between inner duct size and inner duct size
duct_tol=0.3; //[0.2:0.1:0.6]
//The gap between the adapter and the fan, if the item is not up to an automatic size, the script will use the automatic size.
adapter_fan_gap=5; //[5:1:200]
//gap x offset from center [center=0]
gap_xoffset=40; //[0:1:200]
//gap y offset from center [center=0]
gap_yoffset=0; //[0:1:200]

/*[Hidden]*/
size=25;
bolt_c2c=20;
hole_dia=2.9;
//do not edit this section
fan25=[[size,25], [bolt_c2c,20], [hole_dia,2.9]];
fan30=[[size,30], [bolt_c2c,24], [hole_dia,3.3]];
fan40=[[size,40], [bolt_c2c,32], [hole_dia,3.4]];
fan50=[[size,50], [bolt_c2c,40], [hole_dia,4.3]];
fan60=[[size,60], [bolt_c2c,50], [hole_dia,4.3]];
fan70=[[size,70], [bolt_c2c,61.5], [hole_dia,4.3]];
fan80=[[size,80], [bolt_c2c,72], [hole_dia,4.3]];
fan92=[[size,92], [bolt_c2c,82.5], [hole_dia,4.3]];
fan120=[[size,120], [bolt_c2c,105], [hole_dia,4.3]];
fan140=[[size,140], [bolt_c2c,126], [hole_dia,4.3]];
fan_list=[fan25, fan30, fan40, fan50, fan60, fan70, fan80, fan92, fan120, fan140];

//// transform var----
adapter_size=fan_list[adapter_select];
asize=lookup(size, adapter_size);
ac2c=lookup(bolt_c2c, adapter_size);
ahole_rad=lookup(hole_dia, adapter_size)/2;
acorner_rad=(asize-ac2c)/2;
ainner_rad=asize/2-min_border;

fan_size=fan_list[fan_select];
fsize=lookup(size, fan_size);
fc2c=lookup(bolt_c2c, fan_size);
fhole_rad=lookup(hole_dia, fan_size)/2;
fcorner_rad=(fsize-fc2c)/2;
finner_rad=fsize/2-min_border;
cone_hi=max(adapter_fan_gap, ((asize-fsize)/2+max(gap_xoffset,gap_yoffset))*sin(45));
echo("cone_hi", cone_hi);

////----main----
/*translate([fsize*2*0-5, -5, adapter_thi+adapter_fan_gap+duct_hi*2+fan_thi-1])
rotate([0, 180, 0])
small_fan();
adapter_plate();
cone();
//*/

if (part==1)
  print_test();
if (part==2)
  small_fan();
if (part==3){
  adapter_plate();
  cone();
}//i
if (part==4){
  translate([fsize*0.55, 0, 0])
    small_fan();
  translate([-asize*0.55, 0, 0]){
    adapter_plate();
    cone();
  }//t
}//i
////----end main----

module print_test(){
  color("Moccasin")
  translate([-fsize*0.6, 0, duct_hi/2])
  difference(){
//outer duct
    cylinder(h=duct_hi, r=fsize/2, center=true);
//cut outer duct
    cylinder(h=duct_hi+0.01, r=fsize/2-min_border, center=true);
  }//d

  color("yellow")
  translate([fsize*0.6, 0, fan_thi/2+duct_hi/2])
  difference(){
//inner duct
    cylinder(h=fan_thi+duct_hi, r=fsize/2-min_border-duct_tol, center=true);
//cut inner duct
    cylinder(h=fan_thi+duct_hi+0.01, r=fsize/2-min_border*2-duct_tol, center=true);
    }//d
}//m
//

module adapter_plate(){
  color("tan")
  difference(){
    union(){
//fan plate
      translate([0, 0, 0])
        frame(ac2c, adapter_thi, ainner_rad, acorner_rad, ahole_rad);
//circular grill
      if (adapter_grill_type==1)
        translate([0, 0, grill_thi/2])
          circular_grill(asize, ainner_rad);
//line grill
      else if (adapter_grill_type==2)
        translate([0, 0, grill_thi/2])
          line_grill(asize, ac2c);
    }//u
  }//d
}//m
//

module small_fan(){
  color("yellow")
  difference(){
    union(){
//fan plate
      translate([0, 0, 0])
        frame(fc2c, fan_thi, finner_rad-duct_tol, fcorner_rad-duct_tol, fhole_rad);
//circular grill
      if (fan_grill_type==1)
        translate([0, 0, grill_thi/2])
          circular_grill(fsize, finner_rad);
//line grill
      if (fan_grill_type==2)
        translate([0, 0, grill_thi/2])
          line_grill(fsize, fc2c);
    }//u
  }//d

  color("yellow")
  translate([0, 0, fan_thi/2+duct_hi/2])
  difference(){
//inner duct
    cylinder(h=fan_thi+duct_hi, r=fsize/2-min_border-duct_tol, center=true);
//cut inner duct
    cylinder(h=fan_thi+duct_hi+0.01, r=fsize/2-min_border*2-duct_tol, center=true);
    }//d
}//m
//

module cone(){
  color("orange")
  difference(){
//cone
    hull(){
      translate([0, 0, adapter_thi])
        cylinder(h=0.001, r=asize/2, center=true);
      translate([gap_xoffset,gap_yoffset, adapter_thi+cone_hi])
        cylinder(h=0.001, r=fsize/2, center=true);
    }//h
//cut cone
    hull(){
      translate([0, 0, adapter_thi])
        cylinder(h=0.003, r=asize/2-min_border, center=true);
      translate([gap_xoffset, gap_yoffset, adapter_thi+cone_hi])
        cylinder(h=0.003, r=fsize/2-min_border, center=true);
    }//h
  }//d

  color("orange")
  translate([gap_xoffset, gap_yoffset, adapter_thi+cone_hi+duct_hi/2])
  difference(){
//outer duct
    cylinder(h=duct_hi, r=fsize/2, center=true);
//cut outer duct
    cylinder(h=duct_hi+0.01, r=fsize/2-min_border, center=true);
  }//d
}//m
//

module circular_grill(size,rad){
//diagonal line
  for (i=[45,135])
    rotate([0,0,i])
      cube([size-min_border, grill_size*1.5, grill_thi], true);
//circular line
  for (i = [rad-grill_size*grill_sp_factor:-grill_size*grill_sp_factor:0])
    difference(){
      cylinder(h=grill_thi, r=i+grill_size/2, center=true);
      if (i > grill_size*4)
        cylinder(h=grill_thi+0.01, r=i-grill_size/2, center=true);
    }//d
}//m
//

module line_grill(size, c2c){
//x axis line
  for (i = [0:grill_size*grill_sp_factor:c2c/2])
    translate([0 , i, 0])
      cube([size-min_border, grill_size, grill_thi], true);
  for (i = [0:-grill_size*grill_sp_factor:-c2c/2])
    translate([0, i, 0])
      cube([size-min_border, grill_size, grill_thi], true);
//y axis line
  if (size > 80)
    for (i = [-size/6, size/6])
      translate([i, 0, 0])
        cube([grill_size*1.5, size-min_border, grill_thi], true);
  else if (size > 40)
    cube([grill_size*1.5, size-min_border, grill_thi], true);
}//m
//

module frame(c2c, hi, inner_rad, edge_rad, hole_rad){
  difference(){
    hull(){
      for (i = [-c2c/2,c2c/2])
      for (j = [-c2c/2,c2c/2])
        translate([i, j, 0])
          linear_extrude(height=hi)
            circle(r=edge_rad, $fn=24);
    }//h
    for (i = [-c2c/2,c2c/2])
    for (j = [-c2c/2,c2c/2])
      translate([i, j, -0.01])
        linear_extrude(height=hi+0.02)
          circle(r=hole_rad, $fn=24);
    translate([0, 0, -0.01])
      linear_extrude(height=hi+0.02)
        circle(r=inner_rad);
  }//d
}//m
//
```


[User]
I'd like to design a spinning top in OpenSCAD, but I want to make it in two pieces that fit together, sort of like the example I'm attaching, but I want to make the parameters customizable. The idea is to have the seam mostly hidden, on the bottom side.

I need to be able to adjust the level of detail for the curved parts and the rotational extrusion of the main shape. There should also be parameters to control the gap between the two pieces both along the z-axis and along the x-y plane, for print tolerance. The attached example uses a combination of `rotate_extrude`, `offset`, `polygon`, `circle`, and `square`. I imagine the code should involve similar techniques.

The design has a fairly smooth top and bottom, but there is a "ring" extrusion at the "equator" on the top section of the spinning top, sort of a decorative lip. I want to be able to customize these parts as well. Specifically, I want to control the radius of the top and bottom tips, the edge radius where the middle section connects to the tips, and the amount the extrusion protrudes. The overall scale of the top should be controllable too.

The attached example defines a couple of key variables including `shaft_r`, `edge_r`, `edge_extr`, and `ofs`, but I'd like these to be parameterized as well, and exposed in the customizer.

I want the output to be one, or two, parts. It should be possible to render just the top, just the bottom, or both, as separate parts. This can be controlled by a string variable that defaults to `"both"`.

I want to make sure the code is clean and easy to understand. I also don't want to use any external libraries. I'd like the code to use clear comments to explain what's happening. Can you help me with that?

[Assistant]
spinning_top.scad:
```
// preview[view:south, tilt:top]

// Select part(s) to render
part = "both"; // [both:Top and bottom,top:Top,bot:Bottom]

// Detail for rotational extrusion
fn_lathe = 128;// [4:1:256]

// Detail for the curved parts of the cross section
fn_shape = 128;// [4,8,16,32,64,128,256]

// Gap between parts along Z axis
gap_z = .5; // [0:.05:1.5]

// Gap between parts along X and Y axis
gap_xy = .15; // [0:.05:1]

/* [hidden] */
shaft_r = 1;
edge_r = 1.5;
edge_extr = 5;
ofs = 1.25;

if(part == "bot") {
    bot();
} else
if(part == "top") {
    top();
} else
if(part == "both") {
    translate([15, 0, 11.5])
    translate([0, 0, -gap_z])
    rotate(180, [1, 0, 0])
    bot();

    translate([-20, 0, -9+.25])
    top();
}
if(part == "test") {
    bot();
    top();
}

module bot() {
    intersection() {
        spinning_top();
        translate([0, 0, -gap_z])
        cutout(gap_xy);
    }
}

module top() {
    difference() {
        spinning_top();
        cutout();
    }
}

module cutout(gap=0) {
    translate([0, 0, -5])
    cylinder(r=12-gap, h=11.5+5, $fn=fn_lathe);
}

module spinning_top() {
    difference() {
        rotate_extrude($fn=fn_lathe, angle=360/1)
        difference() {
            offset(r=ofs, $fn=fn_shape)
            difference() {
                union() {
                    color("green")
                    polygon(points=[
                        [0, 0+2],
                        [shaft_r, 2],
                        [10+shaft_r, 10],
                        [10+shaft_r, 10+edge_r*2],
                        [shaft_r+1, 30+edge_r*2+5],
                        [0, 30+edge_r*2+5]
                    ]);

                    // Bottom tip
                    color("green")
                    translate([0,shaft_r+1])
                    circle(r=shaft_r, $fn=fn_shape);

                    // Top tip
                    color("green")
                    translate([0, 30+edge_r*2+4])
                    circle(r=shaft_r+1, $fn=fn_shape);

                    color("green")
                    translate([10+shaft_r, 10+edge_r])
                    square([edge_extr*2, edge_r*2], center=true);
                }

                color("red")
                translate([10+shaft_r, 2])
                scale([1, .8])
                circle(r=10, $fn=fn_shape);

                color("red")
                translate([10+shaft_r, 30+edge_r*2])
                scale([1, 2])
                circle(r=10, $fn=fn_shape);
            }

            color("red")
            translate([-shaft_r-4, -5])
            square([shaft_r+4, 50]);
        }
    }
}
```


[User]
I want to create a customizable version of the Tower of Hanoi puzzle as an OpenSCAD project. I'd like to be able to adjust the number of rings, the ring thickness, the radius of the rings, the hole in the rings, the base, and number of pins.

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

**General Structure:**

*   The puzzle will consist of a base, a set of vertical pins, and a set of rings that fit onto the pins.
*   Everything should be parametric, so I can easily adjust dimensions.
*   I would like to be able to output just the base/pins, just the rings, or everything combined.

**Rings:**

*   `ring_count`: The number of rings in the puzzle. Should be adjustable (e.g., 3 to 9).
*   `ring_h`: The height/thickness of each ring.
*   `ring_r_min`: The radius of the smallest ring.
*  `ring_r_max`: The radius of the largest ring. The rings should scale linearly between these two radii.
*   `ring_hole_r`: The radius of the hole in the center of each ring.
*   `fillet_r`: The fillet radius for the ring edges.
*   `fillet_fn`: Detail parameter for the ring fillet (number of facets in the circle used to create the fillet).
*   `ring_fn`: Detail parameter for the ring (number of facets for the lathe/extrude).

**Base and Pins:**

*   `base_t`: The thickness of the base.
*   `base_r`: The fillet radius for the base edges.
*   `base_fn`: Detail parameter for the base fillet.
*   `base_ext`: Distance between the biggest ring and the edge of the base.
*   `pin_count`: The number of vertical pins on the base.  Should be adjustable (e.g., 3 to 6).
*   `pin_gap`: The gap between the pin radius and the hole in the rings.
*   `pin_fn`: Detail parameter for the pin (number of facets for the lathe/extrude).
*   The pins should extend past the height of the stacked rings by one ring height to make the top pin easily accessible.
*   The base should be big enough to accommodate all the rings plus some additional `base_ext` around the edges.

**Filleting:**

*   The rings, pins, and the base should have fillets to make printing easier.
*   The fillet radius should be constrained to not cause problems when scaling other dimensions, especially with small rings.

**Output Options:**

*   I'd like to have a parameter called `part` that can be set to:
    *   `"stand"`: To render only the base and pins
    *   `"rings"`: To render only the rings.
    *  `"all"`: To render everything as a full puzzle.
    * `"test"`: To render the assembled puzzle on the pins, not for printing.

**Other Considerations:**

*   I need to be sure the hole in the rings are large enough for the pins to fit easily.
*   The code should also handle the calculation of the pin radius based on the hole radius and the gap, so the pins fit properly in the rings.

I'd like the parameters to have sensible default ranges and step values in the editor, like what you see in the original code.

I'm not using any libraries, so please just use vanilla OpenSCAD for this design. Let me know if you have any questions.

[Assistant]
toh.scad:
```
// preview[view:south, tilt:top]

// Select part to render
part = "all"; // [stand:Stand,rings:Rings,all:Stand and rings,test:Test]

// Number of rings
ring_count = 5; // [3:1:9]

// Height/thickness of rings
ring_h = 5; // [3:1:10]

// Radius of smallest ring
ring_r_min = 8.5; // [7:.5:15]

// Radius of biggest ring
ring_r_max = 18; // [12:.5:30]

// Radius of ring hole.
ring_hole_r = 3.5;  // [2:.1:10]

// Fillet radius for rings and pins
fillet_r = 1.5; // [0:.1:2]

// Thickness of base
base_t = 4; // [3:.5:10]

// Fillet radius of base
base_r = 3; // [0:1:20]

// Distance betw biggest ring and base edge
base_ext = 1.6; // [0:.1:10]

// Number of pins
pin_count = 3;// [3:1:6]

// Gap between pins and rings
pin_gap = .3; // [.2:.05:1]

/* [Detail] */
// Fillet detail for rings and pins
fillet_fn = 32; //[4:2:64]

// Ring lathe detail
ring_fn = 128; //[4:2:256]

// Base fillet detail
base_fn = 32; //[4:2:64]

/* [Hidden] */

// Pin lathe detail
pin_fn = ring_fn; //64; // [4, 8, 16, 32, 64, 128]

// Pin radius
pin_r = ring_hole_r - pin_gap;

// Pin height
pin_h = ring_h*ring_count + ring_h;

// Check that smallest ring diameter is bigger than the hole
ring_r0 = max(ring_r_min, ring_hole_r+1);

// Check that fillet rad isn't too big to cause problems
fillet = min(fillet_r, ring_h/2, pin_r,
    (ring_r0-pin_r-pin_gap-.001)/2);

if(part == "stand") {
    stand();
} else
if(part == "rings") {
    rings();
} else
if(part == "all") {
    rings();
    translate([0, -ring_r_max*2-base_ext-5, base_t])
    stand();
} else
if(part == "test") {
    stand();
    tower();
    //towers();
}

module rings() {
    m = 5;
    cols = pin_count;
    for(i=[0: ring_count-1]) {
        r = floor(i / cols);
        c = i % cols;
        translate([(ring_r_max*2+m)*c-m/2, (ring_r_max*2+m*0)*r, 0])
        ring(r_out=ring_rad(i), r_in=ring_hole_r,
            r_fil=fillet, h=ring_h, fn=ring_fn,
            fil_fn=fillet_fn);
    }
}

module stand() {
    fil = min(ring_r_max+base_ext-.001, base_r);
    dx = 2*(ring_r_max * pin_count - fil + base_ext);
    dy = 2*(ring_r_max + base_ext - fil);

    for(i=[0:pin_count-1]) {
        translate([ring_r_max*2*i, 0, 0])
        pin(r=pin_r, h=pin_h, r_fil=fillet, fn=pin_fn, fil_fn=fillet_fn);
    }

    translate([-dy/2, -dy/2, -base_t])
    linear_extrude(base_t)
    offset(r=fil, $fn=base_fn)
    square([dx, dy]);
}

module pin(r, h, r_fil, fn, fil_fn) {
    fil = min(r_fil, r);

    rotate_extrude($fn=fn)
    {
        difference() {
            square([r+fil, h-fil]);

            translate([r+fil, fil])
            circle(r=fil, $fn=fil_fn);

            translate([r, fil])
            square([fil*2, h]);
        }

        difference() {
            translate([r-fil, h-fil])
            circle(r=fil, $fn=fil_fn);

            translate([-fil*2, 0])
            square([fil*2, h]);
        }

        translate([0, h-fil])
        square([r-fil, fil]);
    }
}

module ring(r_in, r_out, r_fil, h, fn, fil_fn) {
    fil = min(r_fil, h/2-.001);

    rotate_extrude($fn=fn, angle=180*2)
    translate([fil+r_in, fil])
    offset(r=fil, $fn=fil_fn)
    square([r_out-fil*2-r_in, h-fil*2]);
}

function ring_rad(n) =
    ring_r_max -
    n*(ring_r_max-ring_r0)/(ring_count-1);

module tower() {
    for(i=[0: ring_count-1]) {
        translate([0, 0, ring_h*i])
        ring(r_in=ring_hole_r, r_out=ring_rad(i),
            r_fil=fillet, h=ring_h, fn=ring_fn,
            fil_fn=fillet_fn);
    }
}

module towers() {
    for(i=[0:pin_count-1]) {
        translate([ring_r_max*2*i, 0, 0])
        tower();
    }
}

```


[User]
Okay, I'd like to create a parametric OpenSCAD design for a Hilbert cube, where the user can specify the number of iterations, the size of the corner cubes, the gap between corners, and the width of the connecting beams.

Specifically, I'd like the design to work like this:

*   **Iterations:**  This integer value will control the order of the Hilbert curve, similar to what's shown in the provided code where a value of 1 creates the base shape of 8 cubes, a value of 2 creates a pattern with 64 cubes, etc. I'd like to be able to specify 1-4 iterations. It's okay if it breaks at 5 iterations.
*   **Corner Size:** This numeric value (in mm) determines the side length of each individual corner cube.
*   **Gap Size:** This numeric value (in mm) determines the space between the corner cubes. I'd like this to be defined from the edge of a corner to the edge of the next corner, not from the center.
*   **Beam Size:** This numeric value (in mm) determines the width (or side length for a square) of the beams connecting the corners. If this value is set to 0, it should default to the corner size. I want this to be bounded by 0 and the corner size.
*   The connecting beams should be centered between the corner cubes.

I also want the design to be modular, splitting up different functions into separate modules. For example, I need a function that figures out how the locations of the cubes for each corner, as well as the generation of the beams.

I don't need any external libraries for this, please keep it self-contained. I also want the final object to be rendered using the `hull()` function to combine the beams.

Also please include comments explaining your code, I'd like to understand it.

[Assistant]
HilbertAccurate.scad:
```
//Script adapted from http://thomasdiewald.com/blog/?p=561

/* [Main Parameters] */
//Make an n-th order hilbert cube
Iterations = 2;//[1:4]

//How wide the corners are
corner_size = 5;

//How far apart the corners are
gap_size = 5;

//How wide the beams are in between corners, set to 0 to have the beam size equal to the corner size
beam_size = 0;

/* [Hidden] */

//Normalize corner size above 0
real_corner_size = (corner_size > 0)? corner_size: 5;

//Normalize gap size above 0
real_gap_size = (gap_size > 0)? gap_size: real_corner_size;

//Normalize beam size above 0 and less than or equal to corner size
real_beam_size = (beam_size > 0 && beam_size <= corner_size)? beam_size: real_corner_size;

drawhilbert(real_corner_size, real_gap_size, real_beam_size, Iterations);

module drawhilbert(corner_size, gap_size, beam_size, iterations){
    //flatmult = pow(2,iterations+1);//Used in flatten function

    // size = pow(2,iterations) * spacing/5;//Used for consistent spacing

    cubes = hilbert(iterations = iterations);//Generates flat array of numbers

    beam_offset = [for (i=[0:2]) (corner_size - beam_size) / 2];

    cubelocs = [for(i=[0:len(cubes) - 1]) getmulticoords(cubes[i], corner_size, gap_size)];

    for(a = [0:len(cubes)-2]){
        line(cube1=cubelocs[a],
            cube2=cubelocs[a+1],
            corner_size = corner_size,
            beam_width = beam_size,
            beam_offset = beam_offset);
    }//Goes through vector and renders cubes
}

// Render the connection between two corners
 module line(cube1,cube2, corner_size, beam_width, beam_offset){
     translate(cube1) cube(corner_size); //Generates corners
     translate(cube2) cube(corner_size);

    hull(){//Generates beams
        translate(cube1+ beam_offset) cube(beam_width);
        translate(cube2+ beam_offset) cube(beam_width);}
}

/*
This defines the relative locations of each corner
For example, corner 0 is the corner with small X and Z, but large Y
You will notice that this corresponds to the placement of cubes for a basic hilbert cube
So the corners of a 1st-order hilbert cube is this list in order
*/
cornerlocs = [
    [0, 1, 0],
    [0, 1, 1],
    [0, 0, 1],
    [0, 0, 0],
    [1, 0, 0],
    [1, 0, 1],
    [1, 1, 1],
    [1, 1, 0]
];

// Subtract 1 from every element of a 2d array
function sub12d(arr) =
    [for (i = [0:len(arr)-1])
        [for (j = [0:len(arr[i])-1]) arr[i][j] - 1]];

function cat(lists) = [for(L=lists, a=L) a];

function getcoords(position, cornersize, gapsize)=
    position * (cornersize + gapsize);

function getmulticoords(positions, cornersize, gapsize) =
    [for(i=[0:len(positions) - 1]) getcoords(positions[i], cornersize, gapsize)];

/*
This matrix defines how smaller hilbert cubes make up larger ones
Using a 2D hilbert curve as an example

This is a first-order curve:

*-*
| |
* *

We draw this curve as a single stroke:

2-3
| |
1 4

The second order curve will look like this:

***?***
***?***
***?***
??? ???
*** ***
*** ***
*** ***

A 3x3 grouping of stars means that a smaller hilbert curve is located there
The question marks are where two smaller hilbert curves connect to each other

We know that the hilbert curve actually connects like this:

*-* *-*
| | | |
* *-* *
|     |
*-* *-*
  | |
*-* *-*

Within each section, the stroke order goes like this:

2-3 2-3
| | | |
1 4-1 4
|     |
4-3 2-1
  | |
1-2 3-4

BUT the stroke order of the curve as a whole looks like this if you shrink it down:

2-3
| |
1 4

So the stroke order of the 2nd and 3rd sub-hilberts is the same, but the stroke order of 1 and 4 is different. We store the difference in stroke order the following way:

Overlay the original stroke ordering over each sub-Hilbert curve like this:

2-3 2-3
| | | |
1 4-1 4
|     |
2-3 2-3
  | |
1-4 1-4

Now, follow the original stroke ordering and you get:

1-4-3-2-1-2-3-4-1-2-3-4-3-2-1-4

Break that up into a 4x4 matrix for
[
    [1, 4, 3, 2],
    [1, 2, 3, 4],
    [1, 2, 3, 4],
    [3, 2, 1, 4]
]

The following is the same idea in three dimensions
*/

stroke_ordering = sub12d([
    [1, 4, 5, 8, 7, 6, 3, 2],
    [1, 8, 7, 2, 3, 6, 5, 4],
    [1, 8, 7, 2, 3, 6, 5, 4],
    [3, 4, 1, 2, 7, 8, 5, 6],
    [3, 4, 1, 2, 7, 8, 5, 6],
    [5, 4, 3, 6, 7, 2, 1, 8],
    [5, 4, 3, 6, 7, 2, 1, 8],
    [7, 6, 3, 2, 1, 4, 5, 8]
]);

// Recursive function for generating a list of where cube corners should be
// Base case: generate the location that a single corner cube should be
// Recursive case: concatenate the corner cubes of sub-hilberts
// Output: a list of cube coordinates in order of drawing
// baseloc: the corner of this cube closest to the origin
// iterations: how many times to recur
// cornerlocs: for the macro hilbert cube that is being drawn, in what order should
// the corners be drawn in
function hilbert(baseloc = [0,0,0], iterations, cornerlocs = [0,1,2,3,4,5,6,7])=
    let(cubesacross = pow(2, iterations-1))
    (iterations > 0)?
        cat([
            for (i = [0:7])
                hilbert(corner(cornerlocs[i], baseloc, cubesacross),
                            iterations -1,
                            [for (j=[0:7])
                                cornerlocs[stroke_ordering[i][j]]
                            ])

        ]):[baseloc];

// Get the location of a specific corner of the cube
// cnr: The index of the corner
// baseloc: The corner closest to the origin of this hilbert cube
// cubesacross: How large of a cube are we actually drawing (a 2nd-order cube will be larger than a 1st)
function corner(cnr, baseloc, cubesacross) =
    [for (i = [0:2]) baseloc[i] + (cornerlocs[cnr][i] * cubesacross)];

```