[User]
Hi, I'm looking for a design for a customizable furniture foot, similar to the one at codeandmake.com. I need an OpenSCAD design that lets me adjust the dimensions, specifically the top diameter, bottom diameter, bottom bevel radius, and overall height. Also, it should include screw holes that I can customize.

Specifically, I'd like to be able to specify:
* the diameter of the screw hole
* the diameter of the screw head hole
* the material thickness to allow for proper countersinking
* the countersink angle for the screw head

I was also hoping you could do the following:
* Make the screw hole optional, so it's not always there.
* Maybe use variables named something a little more intuitive, like `top_diameter`, `bottom_diameter`, `bevel_radius`, `height`? And similar intuitive names for the screw hole variables.
* Provide some reasonable default values for all the parameters.

I think that's it for now. I don't need any special libraries, just the core OpenSCAD functionality. Thanks!

[Assistant]
codeandmake.com_Foot_v1.1.scad:
```
/*
 * Copyright 2021 Code and Make (codeandmake.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Foot by Code and Make (https://codeandmake.com/)
 *
 * https://codeandmake.com/post/fully-customizable-foot
 *
 * Foot v1.1 (4 May 2021)
 */

/* [Foot] */

// The diameter of the top of the foot
Foot_Top_Diameter = 36; // [5:0.5:80]

// The diameter of the bottom of the foot
Foot_Bottom_Diameter = 32; // [5:0.5:80]

// The bevel radius of the bottom of the foot
Foot_Bottom_Bevel_Radius = 3; // [1:0.5:10]

// The overall height of the foot
Foot_Height = 27; // [10:0.5:80]

/* [Screws] */

// Screw hole diameter
Screw_Hole_Diameter = 5; // [2:0.5:10]

// Screw head hole diameter
Screw_Head_Hole_Diameter = 10; // [2:0.5:15]

// Material thickness (between screw head and base)
Screw_Material_Thickness = 15; // [2:0.5:80]

// Screw countersink angle (82 and 90 are common)
Screw_Countersink_Angle = 90; // [1:0.5:180]

module foot() {
  $fn = 60;
  hignFn = ($preview ? 60 : 200);

  module screwHole(holeDepth, holeDiameter, headDiameter, boreDepth, aboveHoleBoreDepth, sinkAngle) {
    boreDiameter = (holeDiameter > 0 ? max(holeDiameter, headDiameter) : 0);
    countersinkAdjacent = (boreDiameter / 2) / tan(sinkAngle / 2);

    translate([0, 0, -0.001]) {
      // screw hole
      cylinder(holeDepth + 0.002, holeDiameter / 2, holeDiameter / 2, false);

      // countersink
      if (sinkAngle > 0) {
        translate([0, 0, holeDepth - countersinkAdjacent - boreDepth]) {
          cylinder(countersinkAdjacent + 0.002, 0, (boreDiameter / 2), false);
        }

        // above hole and bore
        translate([0, 0, holeDepth - boreDepth]) {
          cylinder(boreDepth + aboveHoleBoreDepth + 0.002, boreDiameter / 2, boreDiameter / 2, false);
        }
      } else {
        // full bore
        cylinder(holeDepth + aboveHoleBoreDepth + 0.002, boreDiameter / 2, boreDiameter / 2, false);
      }
    }
  }

  difference() {
    intersection() {
      hull() {
        translate([0, 0, -1]) {
          cylinder(d = Foot_Top_Diameter, $fn = hignFn);
        }

        translate([0, 0, Foot_Height - Foot_Bottom_Bevel_Radius]) {
          hull() {
            rotate_extrude(convexity=10, $fn = hignFn) {
              translate([max((Foot_Bottom_Diameter / 2) - Foot_Bottom_Bevel_Radius, Foot_Bottom_Bevel_Radius), 0, 0]) {
                circle(r = Foot_Bottom_Bevel_Radius);
              }
            }
          }
        }
      }

      cylinder(d = max(Foot_Top_Diameter, Foot_Bottom_Diameter), h = Foot_Height);
    }

    if(Screw_Material_Thickness <= Foot_Height) {
      translate([0, 0, -0.5]) {
        screwHole(Foot_Height, Screw_Hole_Diameter, Screw_Head_Hole_Diameter, Foot_Height - Screw_Material_Thickness - 0.5, 1, Screw_Countersink_Angle);
      }
    } else {
      echo("WARNING: Screw hole not added because Screw_Material_Thickness is greater than Foot_Height");
    }
  }
}

foot();

```


[User]
Hi, I'm looking for an OpenSCAD design for a case for the BBC micro:bit, version 1 or 2, and its standard battery pack (the kind that's roughly 26x16x53mm). I want it to be parametric so I can adjust things later if needed.

Here are the specific features I'm thinking about:

*   **Orientation:** The case should allow the micro:bit to sit upright when the case is on its side, providing a tilted view of the LEDs and buttons.
*   **Pin Access:** I'd like to be able to connect to the large pins with crocodile clips, but this should be an optional feature - I'd like a parameter to control whether the pin access is there, or not (i.e. whether it has cutouts for access or not).
*   **LED Visibility:** The micro:bit's power LED should remain visible when it's in the case.
*   **Logo Access:** If possible (and optionally) I'd like the touch-sensitive logo on the micro:bit V2 to still be accessible. This should be configurable with a parameter.
*   **Separate Parts:** It would be useful to be able to export the case and lid as separate parts, so I want a parameter for that.
*   **Secure Lid:** The lid should close securely with a "click", but should still be removable with a little effort - I don't want it falling open by accident.
*   **Battery Security:** The battery pack should be held firmly in place.

I envision a main case part that the micro:bit and battery sit inside, and a lid that snaps onto that. I would like the design to use the MCAD library, specifically the `boxes.scad` module, if it's helpful for any of the parts. I'm not sure if I need anything else, but I want to keep the code as clear and easy to understand as possible, rather than optimized. I also prefer to have rounded corners if that's easy to do. Can you help me create these files?

[Assistant]
microbitcase.scad:
```
use <MCAD/boxes.scad>

pins_accessable = 1; // [0:false, 1:true]

logo_accessable = 1; // [0:false, 1:true]

part = 0; // [0:lid + case, 1:lid, 2:case, 3:(just for view) case + battery box, 4:(just for view) case + battery box + microbit, 5:(just for view) all]

/* [Hidden] */

$fa=0.01;
$fs=($preview || part>2) ? 0.5 : 0.25;

module rounded_plate(dimensions,cornerradius) {
  translate(dimensions/2) roundedBox(dimensions, cornerradius, true);
}

// vertically extruded right-angled triangle with sides on x axis and y axis
module wedge(dimensions) {
  x=dimensions[0];y=dimensions[1];z=dimensions[2];
  linear_extrude(height=z) polygon(points=[[0,0], [0,y],[x,0]]);
}

mb_width = 52;
mb_heigth = 42;
mb_thickness = 1.6;

module microbit() {

  module button() union() { color("grey") cube([6,6,4]); color("black") translate([3,3,2.5]) cylinder(h=2,d=4);}

  module logo() {
    offset = 2;
    c1 = [-offset,0,0];
    c2 = [+offset,0,0];
    difference() {
      hull() {
        translate(c1) cylinder(d=5, h=1);
        translate(c2) cylinder(d=5, h=1);
      }
      translate([0,0,-0.5]) hull() {
        translate(c1) cylinder(d=3, h=2);
        translate(c2) cylinder(d=3, h=2);
      }
    }
    translate(c1) cylinder(d=1, h=1);
    translate(c2) cylinder(d=1, h=1);
  }

  union() {
    difference() {
      color("darkgreen") rounded_plate([mb_width,mb_heigth,mb_thickness], 3);
      // Holes
      for(x = [0:4]) {
        translate([4+x*11,7,-0.2]) cylinder(h=2, r=2);
      }
    }
    // Leds
    for(x = [0:4]) for(y = [0:4]) {
      translate([17+x*4.25,11+y*4,1]) cube([1,2,1]);
    }
    // Buttons A and B
    translate([3,18,1.1]) button();
    translate([52-6-3,18,1.1]) button();
    // Reset button
    translate([13,35.9,0.5]) mirror([0,0,1]) button();
    // Power connector with connector and 'cable'
    translate([3,35.9,-6]) union() {
      color("white") cube([8, 6, 6.5]);
      color("lightgrey") translate([1,2,1]) cube([6,6,4.5]);
      color("red") translate([2,8,1]) rotate([0,90,0]) cylinder(h=1.5, r=2);
      color("black") translate([4.5,8,1]) rotate([0,90,0]) cylinder(h=1.5, r=2);
    }
    // USB connector
    translate([22,38,-3]) color("grey") cube([8,5,3]);

    // Power led
    translate([mb_width-19,mb_heigth-3,-0.4]) cube([1,2,1]);

    translate([mb_width/2,mb_heigth-8,mb_thickness - 0.9]) logo();
  }
}

module batterybox() {
  color("darkgrey") rotate([90,0,90]) rounded_plate([26, 16, 53], 2);
}

width=62;
bottom_height=32;
top_height=48;
height=28;
top_offset=-(top_height-bottom_height)/2;
extra_height=0.6;
tolerance=0.1;
angle=atan2((top_height-bottom_height)/2, height-2);
mb_x = (width-mb_width)/2;

module case() {
  rounding=4;
  module outer() {
      module cylinders(dimensions, r) {
        x=dimensions[0]; y=dimensions[1]; z=dimensions[2];
        translate([r,r,0]) cylinder(h=z, r=r);
        translate([x-r,r,0]) cylinder(h=z, r=r);
        translate([r,y-r,0]) cylinder(h=z, r=r);
        translate([x-r,y-r,0]) cylinder(h=z, r=r);
      }
      hull() {
        cylinders([width, bottom_height, 2], 4);
        translate([0, top_offset, height-2]) cylinders([width, top_height, 2 + extra_height], 4);
      }
  }
  module inner() {
    mirror([1,0,0]) translate([-2,2,2]) rotate([0,-90,0]) {
      yoff=4;
      h0 = height-4.99;
      h1 = h0 * 4/7; // or some complex formula taking the actual angle into account
      linear_extrude(height=width-4) {
        polygon(points=[
          [0,0], [h1, -yoff], [h0,-yoff],
          [h0,bottom_height-4+yoff],[h1, bottom_height-4+yoff], [0,bottom_height-4]]);
      }
    }
  }
  module main() {
    difference() {
      outer();
      inner();
    }
  }

  module battery_holder() {
    module holder_side(h) {
      translate([mb_x+10.5,-8,1.5]) {
        difference() {
          union() {
            cube([26, 10, h]);
            translate([13,8.5,15.5]) sphere(r=2.5);
          }
          translate([1.5,-1.5,-0.5]) cube([23, 10, h+1]);
          rotate([angle,0,0]) translate([-1,-2.5,-5]) cube([28,10,h+6]);
        }
      }
    }

    holder_side(19);
    translate([0,30.5,0]) mirror([0,1,0]) difference() {
      holder_side(22);
      translate([7.01,-7,17]) cube([10,10,10]); // for power connector
    }
  }

  module reset_cutout() { // FIXME Hier zit het nog niet helemaal goed?
    translate([mb_x+2, 30, 12]) {
       translate([0.5, 2, 15.5]) translate([0,0,7.5]) rotate([-90,0,0]) rounded_plate([12, 18, 10],1); // for power connector
       translate([10, 0, -15]) cube([23,20,40],1);
    }
  }

  module base() { // support for the microbit
    module side_base() {
      rotate([-90, 0, 0]) wedge([5,12.5,top_height-12]);
      translate([0,-3,-0.2]) cube([4,top_height-12+3,3.5]);
    }
    translate([0, top_offset, 24.99]) {
      translate([1,6,0]) side_base();
      translate([width-1,6,0]) mirror([1,0,0]) side_base();
    }
  }

  module pinaccess() {
      for(x = [0:4]) {
        translate([mb_x+1+x*11,top_offset-1,height-6])
          translate([0,0,10]) rotate([-90,0,0]) rounded_plate([6,10,10],1);
      }
  }

  module lidmount() {
    module inlet() {
      // front
      translate([5, top_offset + 30, height-6]) rotate([0,-90,0]) wedge([10,5,3]);
      // translate([4, top_offset + 30.01, height+4]) rotate([180,90,0]) wedge([10,5,3]);
      translate([2, top_offset + 28.01, height-6]) cube([3,2,5]);
      // back
      translate([2, top_offset + 12, height-6]) cube([3,5,10]);
      // hole for the knob
      translate([2+1-0.9,top_offset + 15,height-2-1.1]) sphere(r=0.9);
    }
    inlet();
    translate([width, 0, 0]) mirror([1,0,0]) inlet();
  }

  difference() {
    union() {
      difference() {
        union() {
          main();
          base();
        }
        reset_cutout();
        translate([(width-54)/2, 2.5, height-8]) cube([54, 26, 16], 1); // room to insert battery

        translate([mb_x-tolerance,-5-tolerance,25]) cube([mb_width+tolerance*2, mb_heigth+tolerance*2, 5]); // microbit board inlet
        translate([-1, -5.01, 26.6]) cube([width+2, 47, 5]); // lid inlet
        lidmount();
      }
      battery_holder();
    }
    if (pins_accessable) pinaccess();
  }
}

module lid() {
  module hooks() {
    module angles() {
      module side() translate([-0.7,0,0]) rotate([0,-35,0]) cube([2,10,5]);
      side();
      mirror([1,0,0]) side();
    }
    difference() {
      union() {
        // front
        translate([5-tolerance, 30-tolerance, -4-extra_height]) rotate([0,-90,0]) wedge([10,5,3-tolerance*2]);
        translate([5-tolerance, 30.75, 6-extra_height]) rotate([180,90,0]) wedge([10,5,3-tolerance*2]);
        // back
        difference() {
          translate([2+tolerance, 12.5+tolerance, -4-extra_height]) cube([2.5-tolerance*3,4.5-tolerance*2,10]);
          translate([5, 10, -5-extra_height]) rotate([0,-90,0]) wedge([10,5,4]);
        }
        translate([2+1+tolerance-0.9,15,-1.1-extra_height]) difference() {sphere(r=0.9);translate([0,-3,-3]) cube([2,6,6]);}
        // color("green") translate([2+1+tolerance -1.5  +0.5+2.5-tolerance*3  ,14,-1.1-extra_height]) cube([0.6,4,3]); // ff afstand controleren
      }
      translate([1,0,1]) cube([10, top_height, 8]);
      translate([1,0,-11.5]) cube([10, top_height, 8]);
      translate([3.25-tolerance/2,12,-7]) angles();
      translate([3.5,28,-7]) angles();
    }
  }

  // gap to access the touch-sensitive logo (V2)
  module logo_gap() {
    union() {
      c1=[width/2-2,37,-1];
      c2=[width/2+2,37,-1];
      hull() {
        translate(c1) cylinder(d=9, h=4);
        translate(c2) cylinder(d=9, h=4);
      }
      translate([0,0,1.5]) hull() {
        translate(c1) cylinder(d1=8.9,d2=12, h=2);
        translate(c2) cylinder(d1=8.9,d2=12, h=2);
      }
    }
  }

  color("red") {
    hooks();
    translate([width, 0, 0]) mirror([1,0,0]) hooks();
    difference() {
      rounded_plate([width, top_height, 2], 4);
      translate([-1,-1,-1]) cube([width+2, 4, 4]);
      translate([5.8,-3,-1]) rounded_plate([width-11.4, 40, 4],2);

      rotate([15,0,0]) translate([0,0.1,-1]) cube([width+2, 3,5]); // make lid placing a bit easier

      if (logo_accessable) logo_gap();
    }

  }
}

// microbit();
// lid();

if (part==0 || part==1) translate([width,42,2]) rotate([0,180,0]) lid();
difference() {
  union() {
    if (part != 1) case();
    if (part >= 3) translate([mb_x,2.25,2]) batterybox();
    if (part >= 4) translate([mb_x,-5,25]) microbit();
    if (part >= 5) translate([0,top_offset+0.01,height-2 + extra_height + 0.01  ]) lid();
  }
  // translate([-80,-15,-5]) color("red") cube([100, 60, 50]);
  // translate([0,top_offset+15-50,-1]) cube([80,50,50]);
  // translate([-10,-10,20]) cube([100,50,50]);
}

// rotate([-90,0,-90]) wedge([10,20,10]);

// rotate([0,90,0]) wedge([20,10,10]);

```


[User]
Hey, I'm looking to create a customizable hook that can attach to round rods. I've seen some designs on Thingiverse but I want to design my own with some specific features.

The basic shape will be either an "S" or a "3" shape. I need to be able to specify the inner diameter of both the top and bottom circles of the hook, as well as the thickness (which is the distance between inner and outer diameters) and the width of the hook. There should be a parameter to control the opening angle of each circle; this is important because I want the top circle to have a smaller opening to snap securely onto a rod. I'd also like the edges of the hook to be filleted so they're rounded off. It would be nice to adjust the resolution of arcs as well (or, maybe, just the fillet resolution, if that makes more sense).

Can you write an OpenSCAD file that includes all of these customizable parameters? I'm not sure exactly how to do the fillet with OpenSCAD and I'd prefer that previews of the model are *not* filleted, because it's computationally expensive. I'd also appreciate it if the preview shows a separate sample with fillets for easy visualization. I think it should use the minkowski function to do the fillet, if you have suggestions that are better, I'm open to suggestions.

[Assistant]
RoundRodHook-V1.0.scad:
```
/*
Round Rod Hook
   by Lyl3

Create a simple circular hook to attach to a round rod.

This code is published at:
https://www.thingiverse.com/thing:4815367
licensed under the Creative Commons - Attribution - Share Alike license (CC BY-SA 3.0)

V0.1 - basic working code
V0.2 - restricted parameters to sane values
V0.3 - allowed angles greater than 90 degrees
V0.4 - added parameter to create S-shaped hook or E-shaped hook
V0.5 - added code to create preview quality or print quality hook
V0.6 - Cleaned up the code making the preview object correctly sized without the fillets
V0.7 - Added scaling adjustment to fillet sphere for accurate overall dimensions using low $fn = 30
V0.8 - Corrected algorithm for non-90-degree lower opening angle on S-shaped hooks
V0.9 - Added parameter for setting the $fn arc resolution
     - Forced disabling of fillets for previews
     - Added a filleted sample for the previews
V1.0 - ready for publication
*/

/* [Hook parameters] */

// The hook can be shaped like an 3 or like an S. Choose which type you want.
shape_of_hook = "S"; // [3,S]

// Enter the inner diameter of the upper circle (mm).
upper_inner_diameter = 20; // [5:0.1:100]

// Enter the inner diameter of the lower circle (mm).
lower_inner_diameter = 20; // [5:0.1:100]

// Enter the thickness of the hook (mm). This is the distance between the inner diameter and the outer diameter.
thickness = 5; // [3:0.1:10]

// Enter the width of the hook (mm). This is the height of the hook as it lays on the print bed.
width = 15; // [3:0.1:40]

// Enter the angle of the upper circle opening (degrees). An angle of 120 degrees makes the opening about 85% of the diameter, so it has to flex open the additional 15% to snap onto a rod with a diameter equal to the inner diameter of the upper circle.
upper_opening_angle = 120; // [0:0.1:150]

// Enter the angle of the lower circle opening (degrees). An angle of 90 degrees makes the opening large enough to hang bulky stuff like towels. Depending on what you plan on hanging, you may want to decrease this.
lower_opening_angle = 90; // [30:0.1:160]

// Enter the fillet radius to round the edges (mm). This cannot be equal or greater than half the thickness or half the width: if it is then the hook will not be created. Creating the fillets is VERY CPU-intensive so they are disabled for previews and will only be created for renders.  A filleted sample is created for the previews.
fillet_radius = 1; // [0:0.1:3]

/* [Advanced parameters] */
// Enter the resolution of arcs. The default value should be fine but you may want to increase it if you're going to print a really big hook. Larger values will increase the rendering time.
$fn = 60;  // [60:120]

/* [Hidden] */

// Set the fillet to 0 if previewing
fillet = ($preview) ? 0 : fillet_radius;

uid = upper_inner_diameter + 2*fillet;
uod = upper_inner_diameter + 2*thickness -2*fillet;
lid = lower_inner_diameter + 2*fillet;
lod = lower_inner_diameter + 2*thickness -2*fillet;
wid = width -2*fillet;

/*
Create a filleted sample
*/

%translate([-(uod+lod)/4,fillet_radius-thickness/2,fillet_radius]) minkowski()
{
    cube([(uod+lod)/2+fillet_radius,thickness-2*fillet_radius,width -2*fillet_radius]);
    sphere(fillet_radius,$fn=30);
}
%translate([(uod+lod)/3.8+fillet_radius,0,0.5]) text ("filleted sample", size=5);
/*
Create the hook using the minkowski function to fillet the edges.
The sphere must be scaled up slightly in Y and Z dimensions due to inaccuracy from low $fn = 30
*/
translate([0,0,fillet]) minkowski()
{
   makeHookShape (type=shape_of_hook, uid=uid, uod=uod, lid=lid, lod=lod, uoa=upper_opening_angle, loa=lower_opening_angle, wid=wid, thick=thickness);
  scale([1,1.0055,1.0055]) sphere(fillet,$fn=30);
}

/*
Create the basic hook shape without any filleted edges
*/
module makeHookShape (type="3", uid=20, uod=30, lid=20, lod=30, uoa=120, loa=90, wid=15, thick=5)
{
  flip = (type == "3") ? 180: 0;
  lift = (type == "3") ? wid: 0;
  rotLower = (type == "3") ? 90-loa: 90;

  translate ([0,0,lift]) rotate([0,flip,0]) linear_extrude(wid) union() {
    translate([0,(upper_inner_diameter+thickness)/2,0]) rotate([0,0,-90])
    difference() {
      circle(d=uod);
      circle(d=uid);
      difference(){
        translate([-uod,0,0]) square(2*uod);
        rotate([0,0,uoa]) square(uod);
        if (uoa < 90)  translate([-uod,0,0]) square(uod);
      }
    }

    translate([0,-(lower_inner_diameter+thickness)/2,0]) rotate([0,0,rotLower])
    difference() {
      circle(d=lod);
      circle(d=lid);
      difference(){
        translate([-lod,0,0]) square(2*lod);
        rotate([0,0,loa]) square(lod);
        if (loa < 90)  translate([-lod,0,0]) square(lod);
      }
    }
  }
}

```


[User]
Hey there, I'm looking to design a fidget toy, specifically a gyroscopic ball with some customizable features, and I'd like to do it all in OpenSCAD. I'd really appreciate it if you could help me with a parametric design.

Essentially, the toy would consist of a series of concentric rings, each able to rotate relative to the next. It starts with a central disc, and then the rings are built outwards from there. The basic structure I envision is that these rings are cross-sections of spherical shells. They each pivot about an axis through the diameter of the sphere, with each axis rotating 90 degrees relative to the previous.

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

*   **Outer Diameter:** The overall diameter of the finished toy (i.e., the diameter of the outermost ring).
*   **Minimum Diameter:** The diameter of the center disc.
*   **Number of Rings:** The number of rings between the center disc and the outer ring.
*   **Ring Gap:** The space between each ring.
*   **Height/Thickness:** The overall height (or thickness) of the rings and the center disc. I imagine this as being the width of each ring's spherical cross-section.
*   **Monogram Text:** An optional text string that can be wrapped around the outer ring. It should be subtracted from the ring (i.e. engraved), not raised.
*   **Disc Decoration:** An option for a decoration on the center disc, like a smiley face or a recycling symbol. I'd like it if there were a few options to choose from here, maybe an integer to select between no decoration (-1), smiley (0), or recycle symbols for PLA, PETG, or ABS (7, 8, 9). If the recycling symbol is selected, I should also be able to control the material type (PLA, PETG, ABS).

For the actual design, I think I want to use a geodesic sphere instead of the built-in OpenSCAD sphere, because it produces a more uniform distribution of triangles. I've seen Jamie Kawabata's geodesic sphere code on Thingiverse and that would probably do the trick.

It would be amazing if you could provide me with OpenSCAD files that incorporate these parameters, including the geodesic sphere code. Thanks!

[Assistant]
fidgetball.scad:
```
/*
Fidget ball:
Customizable gyro toy with optional wrap-around text
by Alex Matulich
April 2021
On Thingiverse: https://www.thingiverse.com/thing:4818784
*/

//---------- Customizer settings ----------

// diameter of outer ring
outer_diameter = 85;
// diamter of center disc
minimum_diameter = 30;
// number of rings from center disc
n_rings = 5;
// gap between rings
ring_gap = 1;
// height (no more than 3/4*minimum_diameter)
height = 16;
// Monogram for outer ring (leave empty for none)
monogram_text = "";
// central disc decoration (-1=blank, 0=smiley, 7=PLA/PETG, 9=ABS)
disc_deco = 0;
// recycle material, used if disc_deco > 0
recycle_matl = "PLA"; // [PLA,PETG,ABS]

//---------- end of Customizer settings ----------

module dummy() {} // force customizer to stop here

//---------- initializations ----------

clearance = 0.25;   // clearance inside the bearings

// ring dimensions
outer_ring_r = 0.5*outer_diameter;  // outer ring radius
min_r = 0.5*minimum_diameter;       // inner disc radius
disctop_r = min_r * cos(asin(0.5*height/min_r)); // radius of top of disc
ring_thickness = (outer_ring_r-min_r - n_rings*ring_gap) / n_rings; // thickness of each ring
stepsize = ring_thickness+ring_gap; // radius increment per ring

// axle and bearing dimensions
conehalfangle = 40;     // half-angle of hinge cone
axleconeht = min(ring_gap+ring_thickness-1, max(ring_gap+0.6*ring_thickness, 0.5*height/tan(conehalfangle)));
axlercone = axleconeht*tan(conehalfangle);
bearingrcone = 0.5*height+1;
bearingconeht = bearingrcone/tan(conehalfangle);

// wraparound text dimensions
monogram_size = 0.5*height;
monogram_font = "Liberation Mono:style=Bold";
monogram_angle=2*atan(0.45*monogram_size/outer_ring_r);

// display the final product!
translate([0,0,0.5*height]) rotate([0,0,45])
    fidget_toy();

//---------- modules ----------

// render the object
module fidget_toy() {
    union() {
        difference() { // outer ring first
            ring(outer_ring_r, 0);
            if (monogram_text != "") monogram();
        }
        // now make rest the rings inward
        for(i=[1:n_rings-1]) let(r = outer_ring_r-i*stepsize) ring(r, i);
        difference() { // central disc
            disc();
            disc_decoration();
        }
    }
}

// central disc decoration (-1=none, 0=smiley, 5,9,etc.=recycle symbol)
module disc_decoration(deco = 0) {

     // recycle symbol
    if (deco > 0) {
        translate([disctop_r/10,0,height/2-1]) recycle_symbol(deco, recycle_matl, ht=2, size=disctop_r*1.1);
        rotate([0,180,0]) translate([disctop_r/10,0,height/2-1]) recycle_symbol(deco, recycle_matl, ht=2, size=disctop_r*1.1);
    }

    // smiley
    else if (deco == 0) {
        translate([-0.037*disctop_r,-0.023*disctop_r,height/2-1]) linear_extrude(2, convexity=6) text("☺", size=2*disctop_r, font="Liberation", halign="center", valign="center");
        rotate([0,180,0]) translate([-0.037*disctop_r,-0.023*disctop_r,height/2-1]) linear_extrude(2, convexity=6) text("☺", size=2*disctop_r, font="Liberation", halign="center", valign="center");
    }
}

// monogram to be subtracted, if any
module monogram() {
    monogram_r = outer_ring_r-1;
    for(c=[0:len(monogram_text)-1])
        let(a=c*monogram_angle+90)
            translate([monogram_r*cos(a), monogram_r*sin(a), -0.5*monogram_size])
            rotate([90,0,90+a])
            linear_extrude(2, convexity=6) text(monogram_text[c], size=monogram_size, font=monogram_font, halign="center", valign="baseline");
}

// ring with axles and bearings
module ring(ringradius, ringnum=0) {
    fn = 3*ringradius;
    axlrot = abs(ringnum/2 - floor(ringnum/2)) < 0.001 ? 0 : 90;
    bearingrot = 90-axlrot;
    difference() {
        intersection() {
            union() {
                difference() {
                    geodesic_sphere(r=ringradius, $fn=fn);
                    geodesic_sphere(r=ringradius-ring_thickness, $fn=fn);
                }
                rotate([0,0,axlrot]) axles(ringradius);
            }
            cube([2*ringradius+1, 2*ringradius+1, height], center=true);
        }
        if (ringnum>0) rotate([0,0,bearingrot]) bearing(ringradius);
    }
}

// conical axles for a ring
module axles(ringradius) {
    translate([0,ringradius-ring_thickness,0])
        rotate([90,0,0])
            cylinder(axleconeht, r1=axlercone, r2=0, $fn=64);
    translate([0,-ringradius+ring_thickness,0])
        rotate([-90,0,0])
            cylinder(axleconeht, r1=axlercone, r2=0, $fn=64);
}

// central disc with bearings
module disc() {
    fn = 3*min_r;
    bearingrot = abs(n_rings/2 - floor(n_rings/2)) < 0.001 ? 90 : 0;
    difference() {
        intersection() {
            geodesic_sphere(r=min_r, $fn=fn);
            cube([2*min_r+1, 2*min_r+1, height], center=true);
        }
        rotate([0,0,bearingrot]) bearing(min_r);
    }
}

// conical axle bearings for a ring
module bearing(ringradius) {
    zoffset = -stepsize-bearingconeht+axleconeht+clearance;
    translate([0,ringradius-ring_thickness,0])
        rotate([90,0,0])
            translate([0,0,zoffset])
                cylinder(bearingconeht, r1=bearingrcone, r2=0, $fn=64);
    translate([0,-ringradius+ring_thickness,0])
        rotate([-90,0,0])
            translate([0,0,zoffset])
                cylinder(bearingconeht, r1=bearingrcone, r2=0, $fn=64);
}

// optional recyling symbol for disc
module recycle_symbol(num=7, name="PETG", ht=1, size=20) {
    t = size/10; //thickness
    rco = size/8; // corner outer radius
    rci = rco-t; // corner inner radius
    rv = size*cos(60); // corner origin radius
    rt = (rv/sin(60)+rci+t/2)/2;
    difference() {
        hull() {
            for(i=[0:1:2]) translate([rv*cos(i*120),rv*sin(i*120),0]) cylinder(ht, r=rco, $fn=16);
        }
        hull() {
            for(i=[0:1:2]) translate([rv*cos(i*120),rv*sin(i*120),-1]) cylinder(ht+2, r=rci, $fn=16);
        }
        // gaps
        for(i=[0:1:2]) translate([rt*cos(i*120+60)-t*cos((i+1)*120+30),rt*sin(i*120+60)-t*sin((i+1)*120+30),-1]) rotate([0,0,-30]) cylinder(ht+2, r=rco, $fn=3);
    }
    // arrowheads
    for(i=[0:1:2]) translate([rt*cos(i*120+60),rt*sin(i*120+60),0]) rotate([0,0,-150]) cylinder(ht, r=rco, $fn=3);
    // text
    rotate([0,0,-90]) linear_extrude(ht,convexity=6) text(text=str(num), size=size/3, font="Liberation Sans:style=Bold", valign="center", halign="center");
    translate([-size*cos(60),0,0]) rotate([0,0,-90]) linear_extrude(ht,convexity=6) text(text=name, size=size/4, font="Liberation Sans:style=Bold", valign="top", halign="center", spacing=1.15);
}

// I did not write the code below.

//==========================================================

// geodesic sphere based on a subdivided icosahedron
// by Jamie Kawabata, April 2018
// On Thingiverse: https://www.thingiverse.com/thing:1484333

//geodesic_sphere(20);
//translate([40, 0, 0]) sphere(20);

// same syntax and semantics as built-in sphere, so should be a drop-in replacement
// it's a bit slow for large numbers of facets
module geodesic_sphere(r=-1, d=-1) {
  // if neither parameter specified, radius is taken to be 1
  rad = r > 0 ? r : d > 0 ? d/2 : 1;

  pentside_pr = 2*sin(36);  // side length compared to radius of a pentagon
  pentheight_pr = sqrt(pentside_pr*pentside_pr - 1);
  // from center of sphere, icosahedron edge subtends this angle
  edge_subtend = 2*atan(pentheight_pr);

  // vertical rotation by 72 degrees
  c72 = cos(72);
  s72 = sin(72);
  function zrot(pt) = [ c72*pt[0]-s72*pt[1], s72*pt[0]+c72*pt[1], pt[2] ];

  // rotation from north to vertex along positive x
  ces = cos(edge_subtend);
  ses = sin(edge_subtend);
  function yrot(pt) = [ ces*pt[0] + ses*pt[2], pt[1], ces*pt[2]-ses*pt[0] ];

  // 12 icosahedron vertices generated from north, south, yrot and zrot
  ic1 = [ 0, 0, 1 ];  // north
  ic2 = yrot(ic1);    // north and +x
  ic3 = zrot(ic2);    // north and +x and +y
  ic4 = zrot(ic3);    // north and -x and +y
  ic5 = zrot(ic4);    // north and -x and -y
  ic6 = zrot(ic5);    // north and +x and -y
  ic12 = [ 0, 0, -1]; // south
  ic10 = yrot(ic12);  // south and -x
  ic11 = zrot(ic10);  // south and -x and -y
  ic7 = zrot(ic11);   // south and +x and -y
  ic8 = zrot(ic7);    // south and +x and +y
  ic9 = zrot(ic8);    // south and -x and +y

  // start with icosahedron, icos[0] is vertices and icos[1] is faces
  icos = [ [ic1, ic2, ic3, ic4, ic5, ic6, ic7, ic8, ic9, ic10, ic11, ic12 ],
    [ [0, 2, 1], [0, 3, 2], [0, 4, 3], [0, 5, 4], [0, 1, 5],
      [1, 2, 7], [2, 3, 8], [3, 4, 9], [4, 5, 10], [5, 1, 6],
      [7, 6, 1], [8, 7, 2], [9, 8, 3], [10, 9, 4], [6, 10, 5],
      [6, 7, 11], [7, 8, 11], [8, 9, 11], [9, 10, 11], [10, 6, 11]]];

  // now for polyhedron subdivision functions

  // given two 3D points on the unit sphere, find the half-way point on the great circle (euclidean midpoint renormalized to be 1 unit away from origin)
  function midpt(p1, p2) =
    let (midx = (p1[0] + p2[0])/2, midy = (p1[1] + p2[1])/2, midz = (p1[2] + p2[2])/2)
    let (midlen = sqrt(midx*midx + midy*midy + midz*midz))
    [ midx/midlen, midy/midlen, midz/midlen ];

  // given a "struct" where pf[0] is vertices and pf[1] is faces, subdivide all faces into 4 faces by dividing each edge in half along a great circle (midpt function) and returns a struct of the same format, i.e. pf[0] is a (larger) list of vertices and pf[1] is a larger list of faces.
  function subdivpf(pf) =
    let (p=pf[0], faces=pf[1])
    [ // for each face, barf out six points
      [ for (f=faces)
          let (p0 = p[f[0]], p1 = p[f[1]], p2=p[f[2]])
            // "identity" for-loop saves having to flatten
            for (outp=[ p0, p1, p2, midpt(p0, p1), midpt(p1, p2), midpt(p0, p2) ]) outp
      ],
      // now, again for each face, spit out four faces that connect those six points
      [ for (i=[0:len(faces)-1])
        let (base = 6*i)  // points generated in multiples of 6
          for (outf =
          [[ base, base+3, base+5],
          [base+3, base+1, base+4],
          [base+5, base+4, base+2],
          [base+3, base+4, base+5]]) outf  // "identity" for-loop saves having to flatten
      ]
    ];

  // recursive wrapper for subdivpf that subdivides "levels" times
  function multi_subdiv_pf(pf, levels) =
    levels == 0 ? pf :
    multi_subdiv_pf(subdivpf(pf), levels-1);

  // subdivision level based on $fa:
  // level 0 has edge angle of edge_subtend so subdivision factor should be edge_subtend/$fa
  // must round up to next power of 2.
  // Take log base 2 of angle ratio and round up to next integer
  ang_levels = ceil(log(edge_subtend/$fa)/log(2));

  // subdivision level based on $fs:
  // icosahedron edge length is rad*2*tan(edge_subtend/2)
  // actually a chord and not circumference but let's say it's close enough
  // subdivision factor should be rad*2*tan(edge_subtend/2)/$fs
  side_levels = ceil(log(rad*2*tan(edge_subtend/2)/$fs)/log(2));

  // subdivision level based on $fn: (fragments around circumference, not total facets)
  // icosahedron circumference around equator is about 5 (level 1 is exactly 10)
  // ratio of requested to equatorial segments is $fn/5
  // level of subdivison is log base 2 of $fn/5
  // round up to the next whole level so we get at least $fn
  facet_levels = ceil(log($fn/5)/log(2));

  // $fn takes precedence, otherwise facet_levels is NaN (-inf) but it's ok because it falls back to $fa or $fs, whichever translates to fewer levels
  levels = $fn ? facet_levels : min(ang_levels, side_levels);

  // subdivide icosahedron by these levels
  subdiv_icos = multi_subdiv_pf(icos, levels);

  scale(rad)
  polyhedron(points=subdiv_icos[0], faces=subdiv_icos[1]);
}

```


[User]
Hey, I'm looking for an OpenSCAD design for a parametric corner square, but it needs to do a bit more than a standard square. I've got a rough idea I'm working with right now, but I'd like to clean it up and make it truly parametric.

Essentially, I need a flat square base with a lip on two adjacent sides. I need the base's dimensions to be adjustable - specifically, both the width and the length of the base. The base should also have an adjustable height. Then, I need the lip on the two sides to also have their own adjustable height, width, and distance from the edges. I also want a feature that allows you to use it as a center finder by extending a miter line that will go right through the corner. This is especially tricky, since I need it to work even if the width and length aren't the same. I also need to be able to adjust the length of that little center finder line that pokes out.

I was imagining it'd work like this: there'd be a rectangular base of `width` and `length`, and then a `base_height`. The lips would be attached on two sides, each with a `lip_height` and `lip_width`, and the lip would be offset from the edge by `lip_distance`. Finally, the center-finding miter cut would extend out by `cut_tip` units.

I'd prefer if it were all in one file, rather than spread over multiple files, but if it would be significantly simpler to use helper files in an established library, I'm open to that. Could you help me with this? I am comfortable using libraries such as BOSL, or BOSL2, but I do not have any existing code that makes use of them.

[Assistant]
ParametricCornerSquare.scad:
```
width = 75;
length = 75;
base_height = 2.4;
lip_height = 2.4;
lip_width = 4;
lip_distance = 5; // "notch width" so you can cut/mark all the way to the corner more easily
cut_tip = 15;

union() {
    difference() {
    cube([width, length, base_height]);
        union(){
        rotate([0,0,90-atan2(width, length)])
        translate([0, 0, -.5])
        cube([width*10, length*10, base_height + 1]);
                translate([width, length, 0])
    rotate([0,0,45])
    cube(cut_tip, center = true);
        }
    }
    translate([0,0,base_height])
            cube([width, lip_width, lip_height]);
    translate([0,0,base_height])
            cube([lip_width, length, lip_height]);

    intersection() {
     translate([0,lip_distance,0])
            cube([lip_width, length - lip_distance, lip_height]);
    translate([0,lip_distance,0])
    rotate([0,0,90-atan2(width, length)])
        cube([(width + length) * 10, (width + length) * 10, lip_height]);
    }
}
```


[User]
I'm looking to create a customizable vase/lampshade-like structure in OpenSCAD. I want a polycylindrical shape that has a few different types of parameters I can control, and I want to be able to control the inner and outer surface separately for different looks.

Specifically, I want to define:

*   **Number of sides:** The polygon's side count.
*   **Total height** (or alternatively number of levels and spacing between levels): The overall height of the structure. It should be possible to specify total height or number of levels + spacing. The levels should be evenly spaced.
*   **Maximum radius:** The distance from the center to the outermost vertex of the polygon at its widest point.
*   **Thickness:** The thickness of the structure.
*   **Base thickness:** The thickness of the base (can be 0).
*   **Twist:** A linear twist (rotation) along the z-axis (degrees per unit height)
*  **Vertical Sine Wave:** The ability to add a sinusoidal wave component to the rotation of each level as you move up the z-axis, by specifying an amplitude (degrees) and period (units).
*   **Radius function**: The ability to control the radius along the z-axis by specifying both a linear taper and a sinusoidal wave along the Z-axis. I'd like to control the slope of the linear taper (change in radius / change in height), the amplitude of the profile wave (unit length), and the frequency of the profile wave (1/units length)

Ideally, the design would be created by generating a series of polygons along the z-axis, then connecting these polygons to form the shape.  I think it should be possible to specify the radius function for both the inner and outer surfaces independently, although a simpler version where it is just applied to both is fine. Also, it should be possible to turn off the radius function parameters. If there is no base, then the inner and outer should just connect and not have a hole at the bottom.

I want to avoid using any specific libraries and would like all of the files in the same directory.

[Assistant]
polycylindricalshade_sine.scad:
```
//number of sides
sides = 7;
//spacing of Z levels
z_spacing = 10;
//maximum radius, center to outer vertex
m_radius = 30;
//thickness (at thickest part)
thickness = 2;
// number of levels
nlevel = 15;
//linear phase angle change, determinesm linear twistiness (degrees/unitlength)
phasediff = 1;
//base thickness, if 0 there is no base.
bthick = 3;
//period of wavyness alongz (unitlength), do not make 0
period = 150;
//amplitude of wavyness (degrees), make 0 if you dont want sine component
amplitude = 13;

module makepolyhelix(level,side,spacing,radius,phase,amp,per){// level = # of levels, side = number of sides of polygon, spacing = length between levels, radius = distance from center to vertex of polygon, phase = number of degrees the polygon is rotated on every level, amp is the amplitude of a sin() component, per is the period of that sin component
    points = [for (i = [0:level]) for (j = [0:(side -1)]) [radius*cos(amp*sin(i*spacing*(360/per)) +i*spacing*phase + j*(360/side)),radius*sin(amp*sin(i*spacing*(360/per)) + i*spacing*phase + j*(360/side)),i*spacing] ];
    //generate two triangular faces for each generation of nested for loop, not sure how to make this more readable
    faces = [for (i = [0:(level-1)]) for (j = [0:(side -1)]) each
        [ [j+(i*side), j+side*(i+1), (j+1)%side+i*side],
        [(j+1)%side+(i*side), j+side*(i+1), ((j+1)%side)+(i+1)*side]]];

    bottomface = [for (i = [0:side-1]) i];
    topface = [for (i = [1:side]) side*(level+1)-i];

    faces2 = concat(faces,[bottomface],[topface]);

    polyhedron(points,faces2);
}

i_radius = m_radius - thickness/cos((360/sides)/2);// calculates inner radius from thickness and outer radius.

difference(){
    makepolyhelix(nlevel,sides,z_spacing,m_radius,phasediff,amplitude,period);
    makepolyhelix(nlevel,sides,z_spacing,i_radius,phasediff,amplitude,period);
}
if (bthick > 0){
mirror([0,0,1]){makepolyhelix(1,sides,bthick,m_radius,-phasediff,-amplitude,period);}
}

```
polycylindricalshade_sine_radiusfunction.scad:
```
//number of sides (theta resolution)
sides = 7;
//total height
total_height = 150;
//maximum radius, center to outer vertex
m_radius = 30;
//thickness (at thickest part)
thickness = 2;
// number of levels (z resolution)
nlevel = 10;
//base thickness, if 0 there is no base.
bthick = 3;
//spacing of Z levels
z_spacing = total_height/nlevel;

//linear phase angle change, determines linear twistiness (degrees/unitlength)
phasediff = 0;
//period of angular wavyness alongz (unitlength), do not make 0
period = 150;
//amplitude of wavyness of phase (degrees), make 0 if you dont want sine component
amplitude = 13;

//slope of vase radius, positive tapers out negative tapers in, 0 is straight (change in radius/change in height)
rslope = .15;
//amplitude of profile wavyness (unitslength)
ramplitude = 5;
//frequency of profile wavyness (1/units length)
rfrequency = 0.006666667;

//phase along z -axis
function PAZ(z,am,pe,lin) = am*sin(z*(360/pe)) +z*lin;

//radius along z - axis
function RAZ(r,z,am,fe,lin) = r + am*sin(z*(360*fe)) + z*lin;

module makepolyhelix(level,side,spacing,radius,phase,amp,per,rphas,ramp,rfeq){// level = # of levels, side = number of sides of polygon, spacing = length between levels, radius = distance from center to vertex of polygon, phase = number of degrees the polygon is rotated on every level, amp is the amplitude of a sin() component, per is the period of that sin component
    points = [for (i = [0:level]) for (j = [0:(side -1)])
        [RAZ(radius,i*spacing,ramp,rfeq,rphas)*cos(PAZ(i*spacing,amp,per,phase)+j*(360/side)),
         RAZ(radius,i*spacing,ramp,rfeq,rphas)*sin(PAZ(i*spacing,amp,per,phase)+ j*(360/side)),
         i*spacing] ];
    //generate two triangular faces for each generation of nested for loop, not sure how to make this more readable
    faces = [for (i = [0:(level-1)]) for (j = [0:(side -1)]) each
        [ [j+(i*side), j+side*(i+1), (j+1)%side+i*side],
        [(j+1)%side+(i*side), j+side*(i+1), ((j+1)%side)+(i+1)*side]]];

    bottomface = [for (i = [0:side-1]) i];
    topface = [for (i = [1:side]) side*(level+1)-i];

    faces2 = concat(faces,[bottomface],[topface]);

    polyhedron(points,faces2);
}

i_radius = m_radius - thickness/cos((360/sides)/2);// calculates inner radius from thickness and outer radius.

difference(){
    makepolyhelix(nlevel,sides,z_spacing,m_radius,phasediff,amplitude,period,rslope,ramplitude,rfrequency);
    makepolyhelix(nlevel,sides,z_spacing,i_radius,phasediff,amplitude,period,rslope,ramplitude,rfrequency);
}
if (bthick > 0){
mirror([0,0,1]){makepolyhelix(1,sides,bthick,m_radius,-phasediff,-amplitude,period,-rslope,-ramplitude,rfrequency);}
}

```


[User]
I'd like to design a vase with a wavy, almost organic feel, but I want to be able to control all the important dimensions and curves. I want the design to be fully parametric, so I can easily modify the shape and size. I saw this code example that does something similar, but I'd rather start from scratch to make sure it's exactly how I want it.

Here's the general idea: I want a vase created by rotating a 2D profile. This profile should have an inner and outer curve, so I can define a consistent wall thickness. I also need to control the vase's base thickness. The overall shape should be able to scale from the bottom to the top, and have a sine wave pattern going up the sides. I'd like the scaling and the waves to be separate so I can control them independently. I'd like a number of parameters to adjust this design.

Specifically, I need the following parameters:
*   **height**: The overall height of the vase.
*   **radius**: The base radius of the vase.
*   **wall_thickness**: The thickness of the vase walls, constant along the height.
*   **base_thickness**: The thickness of the vase base, can be set to zero.
*   **scale**: A factor controlling how much the radius grows from the base to the top of the vase.
*  **waves**: The number of complete sine waves along the height.
*   **wave_amplitude**: The peak-to-peak amplitude of the sine wave.
*   **wave_start_angle**:  The starting angle of the sine wave.
*   **slices**: The number of vertical slices used to construct the profile.
*   **$fn**: The number of sides for the 3D object that results from the rotate\_extrude operation. This should be a good number to make it appear smooth.

I don't need a special base; a simple, flat disk would be perfect.

I would like you to generate an OpenSCAD file that does all of this. No libraries need to be included. I'm thinking the overall structure of the scad file should include:

*   Parameter definitions at the beginning.
*   A function to calculate the offset for the wavy edge.
*   A function to calculate the x and y coordinates for a 2D profile based on vertical slices.
*   A module that draws the 2D profile, consisting of two polygons.
*   The rotate\_extrude module that uses this 2D profile.

[Assistant]
wave_vase.scad:
```
// OpenSCAD code to generate a vase using sine waves.
// thingiverse.com
// written by jamcultur April 2021
// follow https://www.instagram.com/jamcultur/

height = 100;           // [10:1:400]
radius = 25;            // [10:0.5:150]
wall_thickness = 2;     // [1:0.1:10]
base_thickness = 2.4;   // [0:0.1:10]
scale = 0.7;            // [0.25:0.01:4]
waves = 1;              // [0:0.01:10]
wave_amplitude = 8;     // [0:0.1:25]
wave_start_angle = 0;   // [0:0.1:360]
slices = 100;
$fn = 180;

slices_per_mm = slices/height;
offset_per_mm = ((scale-1)*radius)/height;
function offset(y) =(y*offset_per_mm)+sin(((y/height)*(360*waves))+wave_start_angle)*wave_amplitude;
function x(i) = i>slices ? wall_thickness : 0;
function y(i) = i>slices ? height-((i-slices-1)/slices_per_mm) : i/slices_per_mm;

module draw_profile() {
    translate([radius-wall_thickness,0,0]) {
        y_points = [for(i=[0:(slices*2)+1]) y(i)];
        x_points = [for(i=[0:(slices*2)+1]) x(i)+offset(y_points[i])];
        points = [for(i=[0:(slices*2)+1]) [x_points[i], y_points[i]]];
        polygon(points);
    }
    if (base_thickness) square([radius+x(0)+offset(0), base_thickness]);
}

rotate_extrude() draw_profile();
```


[User]
I'd like to create a parametric vase generator in OpenSCAD, kind of like the wave vase one I found on Thingiverse, but with more flexibility in the profile. Instead of just a sine wave for the profile, I'd like to be able to define a series of control points and have the profile follow a smooth curve that passes through them using a spline interpolation method.

The basic idea is still a vase created by rotating a 2D profile around the z-axis, so it should use `rotate_extrude()`. The user should be able to define the following parameters:

*   **height:** The overall height of the vase.
*   **radius:** A nominal radius for the base of the vase (the actual radius will be affected by the profile).
*   **wall_thickness:** The thickness of the vase walls. This should be consistent throughout the vase's height.
*   **profile_points:** A list of 2D coordinates (x, y) that act as control points for the profile. The x values here should correspond to offsets from the nominal radius, so something like `radius + x`. The y values should be the height above the base of the vase. We should require that the first point starts at `y=0`, and the last point ends at `y=height`. There's no requirement that the x values for the control points be monotonically increasing or decreasing.
*   **spline_method:** A way of selecting what kind of spline interpolation method we should use: "linear", "catmull-rom" and "cubic".
*   **slices:** This is similar to the number of slices in the wave_vase, it controls the vertical resolution of the vase profile. Higher values make for smoother curves, but take longer to render.
*   **$fn:** The number of facets used for circular sections. A higher value results in smoother circles but longer render times. 180 or more should be enough to be smooth for most purposes.

The program should draw a 2D profile, similar to the one in wave_vase, and then use `rotate_extrude()` to create the 3D model. It needs to cut out the inside of the vase using `difference()` so we get a hollow shape. Also, it should make sure there's a solid bottom.

Could you generate the necessary `.scad` file for this? I’d like to avoid using any external libraries.

[Assistant]
wave_vase.scad:
```
// OpenSCAD code to generate a vase using sine waves
// instructions at https://www.thingiverse.com/thing:4820713
// written by jamcultur April 2021
// follow https://www.instagram.com/jamcultur/

height = 100;           // [10:1:400]
radius = 25;            // [10:0.5:150]
wall_thickness = 2;     // [1:0.1:10]
scale = 0.7;            // [0.25:0.01:4]
waves = 1;              // [0:0.01:10]
wave_amplitude = 8;     // [0:0.1:25]
wave_start_angle = 0;   // [0:0.1:360]
slices = 100;
$fn = 180;

slices_per_mm = slices/height;
offset_per_mm = ((scale-1)*radius)/height;
function offset(y) =(y*offset_per_mm)+sin(((y/height)*(360*waves))+wave_start_angle)*wave_amplitude;
function x(i) = i>slices ? wall_thickness : 0;
function y(i) = i>slices ? height-((i-slices-1)/slices_per_mm) : i/slices_per_mm;

module draw_profile() {
    y_points = [for(i=[0:(slices*2)+1]) y(i)];
    x_points = [for(i=[0:(slices*2)+1]) x(i)+offset(y_points[i])];
    points = [for(i=[0:(slices*2)+1]) [x_points[i], y_points[i]]];
    difference() {
        translate([radius-wall_thickness,0,0]) polygon(points);
        translate([-radius*6,0,0]) square([radius*6, height]);
    }
    square([radius+(radius+offset(0)<0?0:offset(0)), wall_thickness]);
}

rotate_extrude() draw_profile();
```


[User]
Okay, I'd like to design a parametric propeller generator in OpenSCAD, building off of the basic idea in the provided file, but with much more control over the blade profiles. I'm thinking of something modular, so different blade shapes and hub designs can be easily swapped.

Here's what I'm looking for:

**Core Functionality:**

1.  **Hub Customization:**
    *   Radius, length, and number of facets should still be parametric.
    *   I want to choose between a cylindrical hub, rounded hubs on one or both ends (like in the example), or even a conical hub with independent radii at either end. Let's make this selectable via a variable.
    *   A center hole should be included with a configurable diameter.
    *   I'd like the ability to add keyways or flats to the hub bore.

2.  **Blade Customization:**
    *   Number of blades, length, base width, and base thickness should remain parametric.
    *   Instead of just a rectangle, let's define the blade profile using a series of control points along the length of the blade. Each control point should allow defining its width and thickness. The profile can then be generated using linear or spline interpolation between these points. This will allow creating tapered blades and airfoils by manipulating these control points.
    *   The control points should also allow specifying a rotation angle to create blade twist.
    *   I'd like the ability to easily vary the position of the blade along the hub's length, as a percentage, like in the example, as well as a parameter to adjust the radial offset of the blade base from the hub radius.
    *   Optionally, I'd like the blade base to blend smoothly into the hub using a fillet, with configurable radius.
    *   I need the option to create blades with simple rectangular profile (i.e. straight blades) as in the example, as well as more complex profiles created using the control points and interpolation. This should be controllable via a variable.

3.  **Modular Design:**
    *   The hub and blade designs should be modular functions/modules, allowing for them to be combined in different ways.
    *   I'd like to be able to define separate modules for creating the overall shape of the blade and for generating the twist and offset to be applied, to allow for maximum reusability.

4.  **Output:**
    *   The final output should be a solid 3D model of the propeller.

**Extra Features (If Possible):**

*   Ability to add a spinner/nose cone to the hub.
*   Different interpolation methods for the blade profile, e.g. linear, cubic splines
*   Control over the leading and trailing edge shapes of the blade profile.

**Libraries:**

I expect this might involve some complex geometry generation, but I don't expect any outside library beyond the standard OpenSCAD library should be necessary. I also don't expect you to include any .scad files for anything more than this propeller generator and its associated sub-modules.

Basically, I want a really flexible propeller generator that goes beyond the basics. Thanks!

[Assistant]
PropellerMaker.scad:
```
//https://www.thingiverse.com/thing:4822751//
//David Kiefer//
//Revised 4/21/21 - added separate blade facet control and blade position control, comments

/* [Hub Properties] */
Hub_Radius = 3; //[1:10]
Hub_Length = 12; //[3:30]
Hub_Facets = 100; //[3:3000]
Hub_Center_Hole=0.5; //[0.01:0.01:5]
Rounded_Hub=true;
/* [Blade Properties] */
Blades=3; //[2:150]
Blade_Length=46; //[10:100]
Blade_Width=5; //[1:10]
Blade_Thickness=0.8; //[0.1:0.1:10]
Blade_Facets = 100; //[3:500]
//Rotation down legth of blade
Blade_Rotation=45; //[0:90]
//Percentage of Hub Length for centerline of blade
Blade_Position=50; //[1:100]

/* [Hidden] */
angle = 0;

difference(){
union(){
MakeHub();
MakeBlades();
}{
 translate([0,0,-0.1])
cylinder(Hub_Length*1.6, Hub_Center_Hole,Hub_Center_Hole,$fn=Hub_Facets);
        }
    }

module MakeHub() {
    union(){
    translate([0,0,Hub_Length]/2){
    cylinder(Hub_Length, Hub_Radius, Hub_Radius, true, $fn=Hub_Facets);
    }
    if (Rounded_Hub){
    translate([0,0,Hub_Length]){
    sphere(r=Hub_Radius, $fn=Hub_Facets);
    }
}
}
}

module MakeBlades(){
    for (c = [1:Blades])
        MakeBlade(c*(360/Blades));
    }

module MakeBlade(angle){
    echo(str("Blade Angle:", angle));
    rotate([0, 0, angle])
    {
    translate([Blade_Length/2,0,Hub_Length *(Blade_Position/100)]){
        rotate(Blade_Rotation,[10 ,0, 0])
        {
        resize([Blade_Length,0,0])
        cylinder(Blade_Thickness, Blade_Width, Blade_Width, $fn=Blade_Facets);
        }
    }
}
}
```


[User]
I'd like to create a customizable magnetic pen holder for my fridge, similar to the one on codeandmake.com. I'm looking for an OpenSCAD design that lets me adjust a few key parameters.

Specifically, I need to be able to control:

*   **Material Thickness:** This should be a general setting for the whole model.
*   **Pen Hole:** I'd like to specify both the diameter and the depth of the pen hole.
*   **Magnet Holes:** I'll need to set the diameter and depth of the holes for the magnets. I plan to use two magnets.

It would be great if the design incorporated a rounded cutout on the front and little ridges on the top and bottom where the pen holder meets the fridge. The magnets should be placed on the opposite side of the pen holder from where the pen goes.

I'm hoping the design will be suitable for 3D printing, so please consider that when creating the shapes.

Could you please generate the OpenSCAD code for this? I don't have any specific libraries in mind; use what's most appropriate for this design.

[Assistant]
codeandmake.com_Pen_Holder_Fridge_Magnet_v1.0.scad:
```
/*
 * Copyright 2021 Code and Make (codeandmake.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * Pen Holder Fridge Magnet by Code and Make (https://codeandmake.com/)
 *
 * https://codeandmake.com/post/pen-holder-fridge-magnet
 *
 * Pen Holder Fridge Magnet v1.0 (10 April 2021)
 */

/* [General] */

// The minimum thickness of the material
Material_Thickness = 2; // [1:0.1:5]

/* [Pen Hole] */

// Diameter of the pen hole
Pen_Hole_Diameter = 14; // [5:0.1:25]

// Total depth of the pen hole
Pen_Hole_Depth = 70; // [25:1:150]

/* [Magnet Holes] */

// Diameter of the magnet hole
Magnet_Hole_Diameter = 8.5; // [1:0.1:15]

// Depth of the magnet hole
Magnet_Hole_Depth = 2.5; // [1:0.1:5]

module penHolder() {
  $fn = 100;

  penHoleDiameter = max(Pen_Hole_Diameter, Magnet_Hole_Diameter);
  cutOutDepth = penHoleDiameter + Material_Thickness;
  penHoleDepth = max(Pen_Hole_Depth, (cutOutDepth * 2) + penHoleDiameter + (Material_Thickness * 2));
  baseWidth = penHoleDiameter + (Material_Thickness * 2);
  baseHeight = penHoleDepth + Material_Thickness;
  cutOutHeight = penHoleDepth - penHoleDiameter - (Material_Thickness * 2);

  module pen() {
    cylinder(d1 = 0, d2 = penHoleDiameter, h = penHoleDiameter + 0.01);
    translate([0, 0, penHoleDiameter]) {
      cylinder(d = penHoleDiameter, h = penHoleDepth - penHoleDiameter + 1);
    }
  }

  module base() {
    translate([0, 0, -Material_Thickness]) {
      cylinder(d = baseWidth, h = baseHeight);
      translate([-baseWidth / 2, 0, 0]) {
        cube(size=[baseWidth, (penHoleDiameter / 2) + Material_Thickness + Magnet_Hole_Depth, baseHeight]);
      }
    }
  }

  module frontCutOut() {
    translate([0, -(penHoleDiameter / 2) - Material_Thickness, cutOutDepth + penHoleDiameter]) {
      hull() {
        // bottom
        rotate([0, 90, 0]) {
          cylinder(r=cutOutDepth, h = baseWidth + 1, center = true);
        }

        translate([0, 0, cutOutHeight - (cutOutDepth * 2)]) {
          // top
          rotate([0, 90, 0]) {
            cylinder(r=cutOutDepth, h = baseWidth + 1, center = true);
          }
        }
      }
    }
  }

  module magnetHole() {
    rotate([270, 0, 0]) {
      cylinder(d = Magnet_Hole_Diameter, h = Magnet_Hole_Depth + 1);
    }
  }

  module magnetHoles() {
    // bottom
    translate([0, (penHoleDiameter / 2) + Material_Thickness, (baseWidth / 2) - Material_Thickness]) {
      magnetHole();
    }

    // top
    translate([0, (penHoleDiameter / 2) + Material_Thickness, baseHeight - (baseWidth / 2) - Material_Thickness]) {
      magnetHole();
    }
  }

  module ridge() {
    rotate_extrude(convexity = 10) {
      translate([(penHoleDiameter / 2) + (Material_Thickness / 2), 0, 0]) {
        circle(d = Material_Thickness);
      }
    }
  }

  module ridges() {
    translate([0, 0, penHoleDiameter]) {
      ridge();
    }

    translate([0, 0, baseHeight - Material_Thickness]) {
      ridge();
    }
  }

  rotate([-90, 0, 0]) {
    difference() {
      base();
      pen();
      frontCutOut();
      magnetHoles();
    }

    ridges();
  }
}

penHolder();
```


[User]
I'd like to create a stand that can hold multiple laptops, tablets, and phones vertically, like a mail organizer. I want it to grip to the edge of my desk, so it's held securely in place without needing screws or glue (though a screw hole for optional use would be great).

Here are the key features I'm looking for:

*   **Adjustable Slots:** I need to be able to specify the number of slots and the width of each slot. This way, I can customize it for the specific devices I own. I'd like to be able to provide a list of the widths, and the design should automatically create the correct slots based on that.
*   **Desk Grip:** The grip should have a thickness that's customizable to fit different desk thicknesses. It should also have a customizable width that determines how far the grip extends under the desk. I'd like to be able to also set a "wall thickness" of the grip section to ensure it has enough structural integrity.
*   **Overall Shape:** The main body of the stand should have a curved shape. Specifically, I was envisioning a section of a hollowed-out sphere, like a quadrant with the front chopped off. The depth of the stand, the thickness of the material, and the radius of that sphere section should all be customizable.
*   **Side Mounting:** I need to be able to mount this on either the left or the right side of the table. I'd want a simple setting for that to switch it over. I'd also like a setting for which side (front or back) the desk grip is attached to.
*  **Parameters**: I need to be able to set all of the dimensions as parameters, so that I can easily modify the stand as needed. I think things like overall width and depth should be settable, as well as the slot thicknesses and the spacing between slots.

I think this can all be handled with a single parametric OpenSCAD file. I'm not familiar with any specific external libraries, so I think just the standard OpenSCAD functionality will be sufficient. Let me know if that's not the case!

[Assistant]
laptop_stand.scad:
```
$fn=100;

WIDTH=100;
DEPTH=200;

// The thickness of the side of the object
SIDE_THICKNESS=10;

// The thickness of the bottom of the object.
BOTTOM_THICKNESS=3;

// The ratio of the outer diameter to the inner diameter
// of the curved side of the object (unitless, should be >1)
EDGE_THICKNESS=1.25;

// Sizes of each of the device slots
// You can have any number of slots, but their total thickness (plus the
// thickness of the spaces between the slots) must be less than DEPTH
// Reference thicknesses:
// 2014-2015 Macbook Pros 18mm
// 2016-2019 (Touchbar) Macbook Pros: 14.9mm 13", 16.2mm 16"
// 2020 M1 Macbook 15.6mm
// iPad 7.5mm
// iPad Pro 5.9mm
// Kindle Paperwhite 8.2mm
// iphone XR 8.3mm
// Don't forget to measure your devices including case!
SLOTS=[24, 18.5, 18.5, 16, 9.5, 25];

// Space between each slot
SLOT_SPACER=20;

// The "table grip" is the part that holds it fast to the table
// If the thickness is correct for your table, it will hold on without any
// fasteners. There's also a screw hole if needed.

// Thickness of the table
TABLE_THICKNESS=32;

// Width of the bottom part of the table grip that holds it against the bottom of
// your table
TABLE_GRIP_WIDTH=30;

// Depth of the table grip
TABLE_GRIP_DEPTH=100;

// Thickness of the bottom and side walls of the table grip
TABLE_GRIP_THICKNESS=3;

// Radius of the screw hole in the bottom of the table grip
SCREW_HOLE_RADIUS=1.5;

// Whether to put the table grip on the small side or large side of the object
// You would almost always want it on the large side for stability
GRIP_LARGE_SIDE=1;

// This mirrors the object left-to-right
// To mount on the RIGHT side of a table, set to 1
// To mount on the LEFT side of a table, set to 0
MOUNT_RIGHT=1;

// One quadrant of a 100mm diameter sphere, with the front 50mm chopped off,
// which is the overall shape of the object
module spheresec() {
    difference() {
        sphere(r=100);
        //bottom
        translate([-100,-100,-200]) cube(size=200);
        //left
        translate([-100,-200,0]) cube(size=200);
        //back
        translate([0,0,0]) cube(size=200);
        //right
        translate([-100,50,0]) cube(size=200);
    }
}

// Make the sphere quadrant into a stretched, hollow shell
module shell() {
    difference() {
        scale([1, DEPTH/50,1]) spheresec();
        translate([-SIDE_THICKNESS,0,BOTTOM_THICKNESS])
            scale([1/EDGE_THICKNESS, DEPTH/50, 1/EDGE_THICKNESS])  spheresec();
    }
}

// Slots for devices
module upper() {
    difference() {
        shell();
        slots(SLOTS,20);
    }
}

// Recursive function that actually creates the slots
// Because the Y position of each slot is actually based on the previous
// slot, this has to be a recursive function.
module slots(lst, y) {
    translate([-1000,y,BOTTOM_THICKNESS]) cube([2000, lst[0], 2000]);
    if (len(lst)>1) {
        slots([for (i = [1:(len(lst)-1)]) lst[i]],
            y + lst[0] + SLOT_SPACER);
    }
}

// Grippy part that holds on to the desk (with screw hole)
module table_grip() {
    difference() {
        translate([-TABLE_GRIP_DEPTH, 0, -TABLE_THICKNESS])
            cube([TABLE_GRIP_DEPTH, TABLE_GRIP_WIDTH, TABLE_THICKNESS]);
        translate([-TABLE_GRIP_DEPTH,
                    TABLE_GRIP_THICKNESS,
                   -(TABLE_THICKNESS-TABLE_GRIP_THICKNESS)])
            cube([TABLE_GRIP_DEPTH,
                  TABLE_GRIP_WIDTH,
                  TABLE_THICKNESS - TABLE_GRIP_THICKNESS]);
    }
}

// Screw hole on the grip if needed
module screw_hole() {
        translate([-TABLE_GRIP_DEPTH/2,
                TABLE_GRIP_WIDTH/2,
                -(TABLE_THICKNESS)])
                cylinder(r=SCREW_HOLE_RADIUS, h=TABLE_GRIP_THICKNESS);
}

module lower() {
    if (GRIP_LARGE_SIDE) {
        table_grip();
    } else {
        translate([-WIDTH, DEPTH, 0]) rotate([0,0,180]) table_grip();
    }
}

module laptop_stand_unmirrored() {
    union(){
        upper();
        difference() {
            lower();
            screw_hole();
        }
    }
}

// Combine the upper and lower parts
module laptop_stand() {
    if (MOUNT_RIGHT) {
        laptop_stand_unmirrored();
    } else {
        mirror() laptop_stand_unmirrored();
    }
}

laptop_stand();

```


[User]
Hey, I'm looking to create a parametric OpenSCAD file for a bed leveling test print, and I want it to be really flexible. I've got a basic idea, and I'd like to see if you could help me generate the code.

Basically, I want to print a grid of pads with perimeters around them. I need to be able to control things like the size of my print bed, the number of pads, the size of each pad, the number of perimeters, and other relevant dimensions.

Here’s what I have in mind, parameter by parameter:

*   **Unit System**: I want to be able to specify the unit system to use for the dimensions, ideally with options like "mm", "cm", and "in". The output STL should be in mm regardless.
*   **Bed Dimensions**: I'll need to specify the X and Y dimensions of my print bed.
*   **Number of Pads**: I want to control the number of pads in both the X and Y directions using two parameters, `Px` and `Py`.
*   **Pad Dimensions**: Need parameters to define the edge length (side) and the height of each pad, let's call them `Edge` and `Height`.
*   **Number of Perimeters**:  I need to be able to specify how many perimeters will be around each pad, using a parameter `Pn`.
*   **Perimeter Dimensions**: Two parameters needed here: `Offset` for the distance between the perimeters and the pad (also between perimeters), and `Width` for the width of each perimeter.
*   **Other Dimensions**: I need a `Margin` parameter that controls the distance between the pads and the edges of the bed.

I'd also like for the code to handle the scaling of all these parameters internally based on the user-selected unit system, ensuring that the final STL output is always in millimeters. I don't want to handle the conversion myself in the script.

I envision that each pad will have a set of perimeters generated using the `Pn`, `Offset`, and `Width` parameters, and then a central square.  The perimeters should be created using `difference()` with a larger cube and a smaller cube.  The perimeters should have a different height than the central square.

I don't think I need any special libraries for this - just the built-in OpenSCAD functions. Can you generate an OpenSCAD file that does this?

[Assistant]
Bed_Levelling.scad:
```
// This prints a grid of pads to test bed leveling.
// $fn=250; // only needed for when the part is filleted

/*[Units]*/
// The STL will be in mm because OpenSCAD exports in mm.
UnitSystem="mm"; // ["cm","mm", "in"]

/*[Bed Dimensions]*/
X=180; Y=165;
/*[Number of Pads]*/
Px=3; Py=3;
/*[Pad Dimensions]*/
Edge=20; Height=0.2;
/*[Number of Perimeters]*/
Pn=2;
/*[Perimeter Dimensions]*/
Offset=0.5; Width = 1;
/*[Other Variables]*/
// Distance from bed extremities
Margin = 10; // [0:.001:100]
// Corner Fillet Radius (as a percent of edge length)

// No More Configurator
// scaling factor
s=
  UnitSystem=="cm"
  ? 1/10
  :UnitSystem=="in"
  ? 1/25.4
  : 1;
// Scaled Values
Xs=X*s;Ys=Y*s;Ms=Margin*s;Es=Edge*s;
Ws=Width*s;Os=Offset*s;Hs=Height*s;
// Place (Locate) each pad
Xl=Xs-(2*Ms)-(Es);Yl=Ys-(2*Ms)-(Es);
// Iteration Factor
Ix=(Xl/(Px-1));Iy=(Yl/(Py-1));

for(a=[0:1:Px-1]){
  for(b=[0:1:Py-1]){
    translate([a*Ix,b*Iy,0]) Pad();
  }
}

// Pad Object Ready for patterning
module Pad() {translate([Ms,Ms,0]){Base();}}
module Base(){
// Subtracted Perimeter Height
Ph=Hs*10;

for (c=[1:1:Pn]){
// Perimeter Outer Edge Length
PEo=Es+(c*2*(Os+Ws));
// Perimeter Inner Edge Length
PEi=PEo-(2*Ws);
// Offset X,Y
oX=(Pn-c)*1*(Ws+Os);
// Perimeter
translate([oX,oX]){
  difference() {
  shape(PEo,Hs);
  translate([Ws,Ws,-Ph/2])   shape(PEi,Ph);
  }
}}

// Inner Square
translate([(Pn)*(Ws+Os),(Pn)*(Ws+Os),0]) shape(Es,Hs);

}

module shape(width,height){
cube([width,width,height]);
}
```


[User]
I'd like to design a parametric pipe cap for 3D printing, and I'd like it to have two versions: a full, single cap, and a split cap that's held together with a screw. The split cap is preferred for a tighter fit, because it can clamp down when the screw is tightened.

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

**Pipe Dimensions:**

*   `Outer_Diameter`: The outer diameter of the pipe.
*   `Inner_Diameter`: The inner diameter of the pipe.
*   `Inner_Clearance`: The clearance between the cap body and the inside of the pipe.
*   `Inner_Size`: The length of the cap that inserts into the pipe (for the full cap body; the two piece version will be adjusted based on angle)
*   `Inner_Bevel`: The bevel on the inside edge of the cap. This should have a maximum value of `Inner_Diameter/4`.

**Cap Outer Dimensions:**

*   `Top_Size`: The thickness of the cap on the outside of the pipe (including any outer bevel).
*   `Top_Bevel`: The bevel on the outside edge of the cap.  This should be less than or equal to `Top_Size`.

**Split Cap & Screw:**

*   `Screw`: A boolean to determine whether a screw hole is included (1 for yes, 0 for no).
*   `Screw_Size`: The size of the screw to be used (options are M3, M4, M5, M6, and M8).
*  `Screw_Cap_Clearance`: The clearance between the screw head and the hole in the cap, on one side.
* `Screw_Clearance_Factor`: A percentage that determines the clearance between the screw body and the hole.  It should be applied to the difference in radii of the screw's major thread diameter and the nut's AC width. Should be 50% or less.
*   `Screw_Head_Type`: The type of screw head (options are: "Sunken", "Cylindrical", "Cylindrical_Hex/Torx", "Hexagonal").
*   `Cut_Angle`: The angle of the cut on the split cap. Options: 30, 45, or 60.
*   `Inner_Size_Factor`: An inner length adjustment as a percentage of `Inner_Size` beyond the minimum required by the cut angle.

I would also like the design to include a text echo in the console with the minimum screw length to use.

I'm using the `MCAD/nuts_and_bolts.scad` library, so you can assume that's available. I'd appreciate it if you could provide the OpenSCAD code for this design. I'd prefer the split cap to be generated in two pieces, so that I could orient them flat on the print bed.

[Assistant]
pipe-cap.scad:
```

/* [Pipe dimensions] */
// Pipe outer diameter
Outer_Diameter = 22;
// Pipe inner diameter
Inner_Diameter = 19.6;
// Clearance between cap body and inside of pipe
Inner_Clearance = 0.5; // [0:0.1:2]
// Cap length inside pipe. (only full body, 2 piece angle cut length is computed)
Inner_Size = 10;
// Cap bevel inside the pipe. Must be <= Inner_Diameter/4.
Inner_Bevel = 1;

/* [Cap outer dimensions] */
// Cap thickness outside the pipe (including bevel)
Top_Size = 2;
// Cap bevel outside the pipe. Must be <= Top_Size
Top_Bevel = 1;

/* [2 Piece angle cut and screw hole] */
// With screw hole?
Screw = 1; // [1:Yes, 0:No]
// Standard metric screw size
Screw_Size = 3; // [3:M3, 4:M4, 5:M5, 6:M6, 8:M8]
// Screw/nut <-> hole clearance (1-side)
Screw_Cap_Clearance = 0.2; // [0.1:0.1:2]
// Screw body <-> hole clearance (1-side, percentage, should be 50% of cap/body difference or less)
Screw_Clearance_Factor = 50; // [5, 10, 20, 30, 40, 50, 60, 75]
// Screw head type
Screw_Head_Type = "Sunken"; // [Sunken, Cylindrical, Cylindrical_Hex/Torx, Hexagonal]
// Cap inner body cut angle
Cut_Angle = 45; // [30,45,60]
// Add inner body length beyond the minimum defined by cut angle. At least 10%.
Inner_Size_Factor = 10; // [10:1:100]
// END of customizer paramters
module __Customizer_Limit__ () {}

include <MCAD/nuts_and_bolts.scad>;

// ***************************************************
// Define working/rendering parameters
$fa=1;
$fs=0.4;
tolerance = +0.001;

// ***************************************************
// Checks
Top_Bevel_value=(Top_Bevel>Top_Size?Top_Size:Top_Bevel);
Inner_Bevel_value=(Inner_Bevel>Inner_Diameter/4?Inner_Diameter/4:Inner_Bevel);
Inner_Size_value=(Screw?Inner_Diameter/tan(Cut_Angle)*(1+Inner_Size_Factor/100):Inner_Size);

total_length=Inner_Size_value+Inner_Bevel_value+Top_Size;

// ***************************************************
// Info
echo("Minimum screw length:",total_length);

// ***************************************************
// Main

if(Screw == 1) {
    pipe_cap__base();
    pipe_cap__retainer();
} else {
    pipe_cap_full();
}

// ***************************************************
module pipe_cap__retainer() {
    translate([Outer_Diameter*2.1,Outer_Diameter,0])
    rotate([0,180,0])
    translate([0,0,-(Top_Size+Inner_Size_value+Inner_Bevel_value)])
    intersection() {
        pipe_cap_full();
        pipe_cap_cut();
    };
};

module pipe_cap__base() {
    translate([Outer_Diameter,Outer_Diameter,0])
    rotate([0,0,180])
    difference() {
        pipe_cap_full();
        pipe_cap_cut();
    };
};

// ***************************************************
// pipe cap body
module pipe_cap_full() {
    if (Screw == 1) {
        difference() {
            pipe_cap();

            translate([0,0,total_length-(Screw_Head_Type=="Cylindrical_Hex/Torx"?METRIC_NUT_THICKNESS[Screw_Size]*2:METRIC_NUT_THICKNESS[Screw_Size])])
        color("white") nutHole(Screw_Size, units=MM, tolerance = Screw_Cap_Clearance, proj = -1);
            translate([0,0,METRIC_NUT_THICKNESS[Screw_Size]])
            color("white") boltCap(Screw_Size, total_length+tolerance, Screw_Cap_Clearance, Screw_Clearance_Factor, Screw_Head_Type, tolerance);
        };
    } else {
        pipe_cap();
    };
};

module pipe_cap() {
    union() {
        union() {
            translate([0,0,Top_Size]) {
                cylinder(d=Inner_Diameter-Inner_Clearance, h=Inner_Size_value+tolerance);
                translate([0,0,Inner_Size_value])
                    color("red") cylinder(d1=Inner_Diameter-Inner_Clearance,d2=Inner_Diameter-Inner_Clearance-2*Inner_Bevel_value,h=Inner_Bevel_value);
            };
        };

        union() {
            translate([0,0,Top_Size-Top_Bevel_value])
                cylinder(d=Outer_Diameter,h=Top_Size-Top_Bevel_value+tolerance);
            color("red") cylinder(d1=Outer_Diameter-2*Top_Bevel_value,d2=Outer_Diameter,h=Top_Bevel_value+tolerance);
        };
    };
};

// cut away template
module pipe_cap_cut() {
    translate([(Inner_Diameter-Inner_Clearance)/2,0,Top_Size+Inner_Size_value*Inner_Size_Factor/100/2])
    rotate([0,-Cut_Angle,0])
    translate([0,-Inner_Diameter,0])
        color("darkgreen") cube([Inner_Diameter*2,Inner_Diameter*2,Inner_Size_value*2]);
};

// ***************************************************
// utilities
module cylinder_outer(height,radius,fn){
   fudge = 1/cos(180/fn);
   cylinder(h=height,r=radius*fudge,$fn=fn);
};

// derived from nuts_and_bolts library
module boltCap(size, length, cap_tolerance = 0.1, body_tolerance_factor = 10, type, tolerance = +0.0001) {
	body_tolerance = (METRIC_NUT_AC_WIDTHS[size]/2 - COURSE_METRIC_BOLT_MAJOR_THREAD_DIAMETERS[size]/2)*body_tolerance_factor/100;
    radius = COURSE_METRIC_BOLT_MAJOR_THREAD_DIAMETERS[size]/2+body_tolerance;
//TODO: proper screw cap values
	capHeight = METRIC_NUT_THICKNESS[size]+cap_tolerance; //METRIC_BOLT_CAP_HEIGHTS[size]+tolerance;
	capRadius = METRIC_NUT_AC_WIDTHS[size]/2+cap_tolerance; //METRIC_BOLT_CAP_RADIUS[size]+tolerance;

    if (type == "Cylindrical") {
        translate([0, 0, -capHeight])
		cylinder(r= capRadius, h=capHeight);
        translate([0,0,-tolerance])
        cylinder(r = radius, h = length + tolerance);
    }
    if (type == "Cylindrical_Hex/Torx") {
        translate([0, 0, -capHeight*2])
		cylinder(r= capRadius, h=capHeight*2);
        translate([0,0,-tolerance])
        cylinder(r = radius, h = length + tolerance);
    }
    if (type == "Sunken") {
        translate([0, 0, -capHeight])
		cylinder(r1= capRadius, r2=radius, h=capHeight);
        translate([0,0,-tolerance])
        cylinder(r = radius, h = length + tolerance);
    }
    if (type == "Hexagonal") {
        translate([0, 0, -capHeight])
		cylinder(r= capRadius, h=capHeight, $fn=6);
        translate([0,0,-tolerance])
        cylinder(r = radius, h = length + tolerance);
    }
};

```


[User]
Okay, I'm looking to create a series of vintage-style knobs, similar to the ones I've seen on old radios and amps. I'd like to be able to adjust several parameters to get the exact look and fit I need.

Here's the general idea:

*   **Main Body:** The knob should have a cylindrical body, but with a slight taper, so it's wider at the base than at the top. I'd like to control the amount of that taper.
*   **Grips/Indents:**  I need indents or serrations around the main body, like finger grips. I want to control how many there are.
*   **Indicator:** There needs to be an indicator, a line, on the top of the knob that allows it to be visually oriented on a rotary control. The width and depth of this line should be adjustable.
*   **Skirt:** The knob needs a "skirt" or flange at the base, a wider circular section that gives it a more substantial look.
*   **Shaft Hole:** The hole in the knob that fits on the potentiometer should be adjustable for both "D" shafts and knurled shafts. I need to be able to define the dimensions of the D shaft (major and minor diameter) and the knurled shaft (diameter and knurl count) and apply a tolerance offset to account for printer inaccuracies. I'd also like an option for no shaft hole at all for decorative purposes.

I'd like a parametric OpenSCAD design, so I can adjust all these things. Specifically:

**Global Parameters:**

*   `Diameter`: Diameter of the main body at the base (excluding skirt).
*   `Height`: Total height of the knob.
*   `IndentCount`: Number of indentations or grips.
*   `Taper`: A value between 0.6 and 1.0 to control the taper from base to top, with 1.0 being a straight cylinder.
*   `LineWidth`: Width of the indicator line.
*  `LineDepth`: Depth of the indicator line.

**Shaft Parameters:**

*   `ShaftType`:  A dropdown to select "None", "D", or "Knurled".
*   `ShaftDepth`: The depth/length of the shaft hole.
*   `ShaftOffset`: An offset to add to the shaft hole to adjust for printing tolerances.
*   `DShaftMajorDiameter`:  The major diameter for a "D" shaft.
*  `DShaftMinorDiameter`: The minor diameter for a "D" shaft.
*   `KnurledShaftMajorDiameter`: The diameter of the knurled shaft.
*   `KnurlCount`: The number of knurls on the knurled shaft.

I'm hoping that I can control the basic overall look and shape with the above parameters.

I am comfortable with how the attached file handles the individual modules/shapes so I don't think it needs changing.

Could you generate an OpenSCAD file for me that accomplishes this? I don't need any specific libraries used (like BOSL) but if it would help you structure the files better, I'm okay with that.

[Assistant]
VintageKnobB.scad:
```
/* [Knob] */

// Main body diameter (at bottom, excluding skirt)
Diameter = 22;
// Total height
Height = 15;
// Number of indents (grips)
IndentCount = 7; // [3:10]
// Taper (top diameter/bottom diameter). 1.0=no taper 0.6=heavy taper
Taper = 0.95; // [0.6:0.01:1.0]
// Width of indicator line
LineWidth = 1.0;
// Depth of indicator line
LineDepth = 0.5;

/* [Shaft] */

ShaftType = "D"; // ["None", "D", "Knurled"]

// Depth (length) of shaft.
ShaftDepth = 11; // [0.0:0.01:50.0]

// X/Y Offset to compensate for shrinking holes when printing.
// Increase if too tight. Decrease if too loose.
ShaftOffset = 0.3; // [-1.0:0.01:1.0]

// Shaft diameter ->O<-
DShaftMajorDiameter = 6.0; // [3.0:0.01:15.0]
// Minor diameter (across cut) ->D<-
DShaftMinorDiameter = 4.2; // [3.0:0.01:15.0]

KnurledShaftMajorDiameter = 6.0; // [3.0:0.01:15.0]
KnurlCount = 18;

// Slant angle for a cylinder with h and diameters d1 and d2
function Slant(h, d1, d2) = atan(0.5*(d1-d2)/h);

// Diameter of a slanted cylinder at height pos (lerp)
function DiameterAt(h, d1, d2, pos) = d1 - (d1-d2)*(pos/h);

module Cone(h, d) {
  cylinder(h=h, d1=d, d2=0);
}

e=0.001; // error term (e.g. for nudging coincident surfaces for CSG)
in=25.4; // in => mm

module Tube(h, d1, d2) {
  difference(){
    cylinder(h=h, d=max(d1,d2));
    translate([0,0,-e]) cylinder(h=h+2*e, d=min(d1,d2));
  };
}

// Cylinder with bevel along top edge
module BeveledCylinder(h, d1, d2, bevel, bevelBottom=false) {
  r2 = d2*0.5;
  intersection() {
    cylinder(h=h, d1=d1, d2=d2);
    translate([0,0,-bevel])
      Cone(h=h+r2, d=2*r2*(1+h/r2));
    if (bevelBottom) {
      translate([0,0,-r2+bevel])
        cylinder(h=h+r2, d1=0, d2=d1*(1+h/r2));}
  };
}

module SquareSerrations(h, d1, d2, fromz, toz, offset=0, count, gapPct=50, rotationDeg, slant) {
  baseD = DiameterAt(h, d1, d2, fromz);
  side = baseD*PI / count * (100-gapPct)/100;
  for (i=[0:count-1])
      rotate([0,0,360/count*i])
        translate([baseD/2+offset, 0, fromz])
          rotate([0, -slant, 0])
            rotate([0,0,rotationDeg])
              translate([-side/2, -side/2, 0])
                cube([side,side,toz-fromz]);
}

// Generates vertical serrations/indents based on count,
// Indents the circle with radius r by count circles
// centered distancePct % off the main circle,
// with radius r2Pct % of the main radius.
// To be useful: r2Pct > distancePct
module Serrations2(h, d1, d2, fromz, toz, count, distancePct, r2Pct, rotationDeg=0, capTop=false, capBottom=false) {
    baseD = DiameterAt(h, d1, d2, fromz);
    distance = 0.01*(baseD/2)*(100+distancePct);
    r2 = 0.01*(baseD/2)*r2Pct; // radius of serrations
    slantDeg = Slant(h,d1,d2);
    for (i=[0:count-1])
      rotate([0,0,360/count*i])
        translate([distance, 0, fromz])
          rotate([0, -slantDeg, 0])
            rotate([0,0,rotationDeg]) {
              cylinder(h=toz-fromz, d=r2*2);
              if (capBottom) { translate([0,0,0]) sphere(r=r2); }
              if (capTop) { translate([0,0,toz-fromz]) sphere(r=r2); }}
}

module Flange(h1, h2, d1, d2, capAt=0) {
  cylinder(d=d1, h=h1);
  intersection() {
    translate([0,0,h1]) cylinder(h=h2, d1=d1, d2=d2);
    if (capAt > 0) cylinder(h=capAt, d=d1);
  }
}

module Dome(h, d, cut=true) {
  x = d / 2;
  r = (x*x+h*h)/(2*h);
  y = r - h;
  difference() {
    translate([0,0,-y]) sphere(r=abs(r)-e);
    if (cut) translate([0,0,-r]) cube([abs(r)*2,abs(r)*2,abs(r)*2], center=true);
  }
}

module LineMarker(fromz, toz, d, widthDeg=5, lengthPct=70) {
  circ = 2*PI*d;
  width = circ*widthDeg/360;
  length = d/2*lengthPct/100;
  translate([-length,-width/2,fromz])
    cube([length,width,toz-fromz]);
}

module DShaft5(h, d=5.9, dmin=4.3) {
    wallThickness = 0.8;
    outerCylinderD = d + 2*wallThickness + 2.0;
    flatXpos = (d/2) - (d-dmin);
    difference() {
        cylinder(h=h, d=outerCylinderD);
        translate([0,0,-e]) {
            difference() {
                Tube(h=h, d1=d + 2*wallThickness, d2=d);
                translate([-d-wallThickness, -wallThickness/2, 0]) cube([d+wallThickness,wallThickness,h]);
        }};
        translate([flatXpos, -d*0.45, 0])
            cube([wallThickness, d*0.9, h]);
    }
}

// Knurled shaft.
// 6mm major diameter, 18 knurls, fits most pots.
module KnurledShaft(h, clearance=0, flare=0, d=6, count=18) {
  spacing = (d-2*clearance) * PI/count * 0.7;
  difference() {
    BeveledCylinder(h-clearance, d+2*clearance, d+2*clearance, 0.5);
    SquareSerrations(h=h, d1=d+2*clearance, d2=d+2*clearance, fromz=0, toz=h,
      count=18, gapPct=30, rotationDeg=45, slant=0);
  }
  if (flare>0) Cone(h=d/2+flare*2, d=d+flare*2);
}

module Shaft() {
    translate([0,0,-e]) {
        if (ShaftType == "D") {
            DShaft5(h=ShaftDepth,
                d=DShaftMajorDiameter+ShaftOffset,
                dmin=DShaftMinorDiameter+ShaftOffset);
        }
        else if (ShaftType == "Knurled") {
            KnurledShaft(h=ShaftDepth,
                clearance=ShaftOffset,
                d=KnurledShaftMajorDiameter,
                count=KnurlCount);
        }
    };
}

// Similar style to A, with line mark instead of dot
module VintageKnobB(h, d) {
  d2=d*Taper; // top diameter (adjust for more/less tapering)
  sm=0.8; // smoothing (rounding) of features on main body
  hcap=d2*0.08; // height of the domed cap
  difference() {
    union() {
      difference() {
        // body
        translate([0,0,sm])
          minkowski(){
            difference() {
              rotate([0,0,180])
                intersection(){
                    cylinder(h=h-2*sm, d1=d, d2=d2);
                    translate([0,0,h-hcap-2*sm]) Dome(h=hcap, d=d2, cut=false);
                  }
                Serrations2(h=h, d1=d, d2=d2, fromz=0, toz=h+1, distancePct=75, // indents
                    count=IndentCount, r2Pct=89);
            };
            sphere(r=sm, $fn=$fn/3);
          };
        // line
        rotate([0,0,180])
        intersection() {
          translate([0,0,h-LineDepth-hcap+e]) {
            difference() {
              translate([0,0,LineDepth+10]) Dome(h=hcap, d=d2, cut=false);
              Dome(h=hcap, d=d2, cut=false);
            };
          }
          union(){
            translate([3,0,0]) cylinder(h=h+10, d=LineWidth, $fn=$fn/3);
            translate([3,-LineWidth/2, 0]) cube([d2/2,LineWidth,h+10]);
          }
        };
      }
      // Skirt
      Flange(h1=d*0.05, h2=d/2, d1=d*1.16, d2=0, capAt=h-hcap);
    };

    // Shaft hole (+ extra cavity around it)
    translate([0,0,-e]) Shaft();

  }
}

$fn = $preview ? 20 : 80;
VintageKnobB(h=Height, d=Diameter);

```


[User]
Hi! I need a parametric OpenSCAD design for a stand for a sampling tube, specifically the kind used in the Hotgen Covid-19 antigen test kits, but I'd like it to be adjustable for different tube sizes.

The basic idea is a circular base with some radial supports and then a cylindrical tube holder in the center. The existing code I have is pretty clunky, so I'm hoping you can start from scratch and make a cleaner, more flexible design.

Specifically, I need the following parameters:

*   **`tube_diameter`**: This controls the inner diameter of the tube holder, and should be adjustable with a default of 13.8 mm (which is what I have now). The tube holder should also have a 1.2mm gap between the inner and outer cylinders.
*   **`base_diameter`**: This will be the diameter of the circular base, with a default of 20mm
*   **`base_height`**: This will be the height of the circular base, with a default of 1.05 mm.
*   **`tube_holder_height`**: This should control the height of the tube-holding cylinder, with a default of 11.05 mm.
*   **`support_arm_width`**: The width of the radial support arms, with a default of 4mm.
*  **`support_arm_depth`**: The depth of the radial support arms, with a default of 1.05mm.
* **`support_arm_count`**:  The number of support arms (the current code uses 5), with a default of 5.
*  **`support_arm_cylinder_radius`**: The radius of the support arm tips, with a default of 5mm

I'd like to use rounded tips for the support arms.

I'd also like the ability to control the `$fn` for all cylinders and circles. This would be controlled with a single parameter called **`segments`** with a default of 200.

Please make the design as clean and modular as possible - I'd like to be able to understand each step easily. Let me know if you have questions. Thanks!

[Assistant]
Covidtest_Stand_customizer.scad:
```
//Tube Diameter
d=13.8; //[10.0:0.1:17.6]

/* [Hidden] */

ri=(d/2);
ra=ri+1.2;

difference()
{
    union()
    {
        translate([0,0,0])
        cylinder(r=10,h=1.05,$fn=50);
        for(i=[0:60:300])
        {
            rotate([0,0,i])
            {
                translate([-2,0,0])
                cube([4,20,1.05]);
                translate([0,20,0])
                cylinder(r=5,h=1.05,$fn=50);
            }
        }

        translate([0,0,0])
        cylinder(r=ra,h=11.05,$fn=200);
    }
    translate([0,0,1.05])
    cylinder(r=ri,h=10.01,$fn=200);
}
```