[User]
Hi, I'm looking for an OpenSCAD design for a customizable, spiral-arm based organizer, like the "Galactic Organizer" on Thingiverse but I'd like you to generate the OpenSCAD code for me from scratch. I want to be able to adjust the following parameters:

*   **Number of arms:** I need to control how many spiral arms there are.
*   **Sockets per arm:** I need to set how many sockets each arm has.
*   **Socket inner diameter:** I need to specify the diameter of the hole in the socket.
*  **Wall thickness:** I need to control the thickness of the walls around each socket.
*   **Slope:**  The arms should slope downwards, and I need a parameter to set the angle of that slope in degrees.
*   **Minimum socket depth:** Since the arms are sloped, the sockets will be deeper at one end.  I need a parameter that controls the minimum depth.

The key feature is that the sockets should be evenly spaced along the involute of a circle. This creates that consistent spacing between arms and between sockets on each arm. So the distance between the centers of adjacent sockets (and arms) should be the same, and the slope creates a sort of ramp.

I'm not really doing any complex CSG here, just cylinders and subtractions, but the involute calculations are critical. I'd like the code to be well commented, for clarity. I don't need any of the standard libraries like MCAD, BOSL, etc..

Could you generate the OpenSCAD file(s) for me? Thanks!

[Assistant]
galactic-organizer.scad:
```
// Galactic Organizer provided by Stellar Innovation
// http://www.thingiverse.com/stellarinnovation/about
// under Creative Commons License, Attribution (BY)
//
// The organizer is built from a given number of spiral arms.
// Each arm consists of a given number of sockets.
// The sockets are evenly spaced on the involute of a circle.
// The innvolute has the property of a constant distance between the arms
// (the same spacing as between sockets of a single arm).

// Number of arms
numArms = 4; // [2:12]

// Number of sockets per arm (the centerpiece is shared)
numSocketsPerArm = 5; // [3:30]

// Diameter of hole (+tolerance)
socketInnerDiameter = 15.5; // [8:0.1:35]

// Wall thickness
wallThickness = 2.4; // [1.2:0.4:4.0]

// Slope (degrees)
slope = 15; // [0:45]

// Minimum socket depth
minSocketDepth = 20; // [1.0:0.5:35.0]

// hole plus wall
socketOuterDiameter = socketInnerDiameter + 2*wallThickness;

// distance center-to-center
distance = socketInnerDiameter + wallThickness;

// angle between arms (in radians), also: normalized distance (radius 1)
deltaTheta = 2*PI/numArms;

// radius of the circle, whose involute makes the spiral arms
radius = distance/deltaTheta;

// first socket is at distance r0 from the origin. A regular polygon, whose
// side is distance (normalized: deltaTheta) is formed by the first socket
// of each arm.
r0 = 0.5*deltaTheta/sin(180/numArms);
firstSocketT = sqrt(r0*r0-1);

degreesPerRadian = 180/PI;

// height difference between adjacent sockets
deltaH = distance*tan(slope);

// total height
slopeLength = numSocketsPerArm - 1; // less "first socket"
height = wallThickness + minSocketDepth + slopeLength*deltaH;

// Equaltion of the involute: x = cos t + t sin t, y = sint t - t cost t
// Given a first t and a normalized distance, find "next t" (normalized: radius=1)

function solveNext(t0, normDist) = let (deg0 = t0*degreesPerRadian,
                                         x0 = cos(deg0) + t0*sin(deg0),
                                         y0 = sin(deg0) - t0*cos(deg0),
                                         t1 = sqrt(2*normDist + t0*t0),
                                         deg1 = t1*degreesPerRadian,
                                         f = t1*t1 - 2*x0*(cos(deg1)+t1*sin(deg1)) - 2*y0*(sin(deg1) - t1*cos(deg1)) +
                                             1 + x0*x0 + y0*y0 - normDist*normDist,
                                         df = 2*t1 - 2*x0*t1*cos(deg1) - 2*y0*t1*sin(deg1))
                                     t1 - f/df;

// First approximation is based on equation of arc length: L = 0.5*radius*t²
// Second approximation by a single Newton-iteration
// 0 = f(t) = (x-x0)² + (y-y0)² - deltaTheta²

// Compute the t parameter of socket (n=0, 1, 2,...)
function socketT(n) = (n==0)? firstSocketT : solveNext(socketT(n-1), deltaTheta);

function pointXY(t) = let (deg = t*degreesPerRadian,
                           x = radius*(cos(deg) + t*sin(deg)),
                           y = radius*(sin(deg) - t*cos(deg)))
                       [x, y];

for (arm=[1:numArms])
  rotate((arm-1)*deltaTheta*degreesPerRadian)
    intersection() {
      Arm();
      RampedSpiral();
    }

module Arm() {
  dh = deltaH*wallThickness/distance;

  for (s = [0:numSocketsPerArm-1]) {
    t = socketT(s);
    translate(pointXY(t)) {
      h = (s == 0)? height : (height + min(0, dh - (s-1)*deltaH));
      p = (s == 0)? minSocketDepth : (minSocketDepth + dh + deltaH);
      Socket(h, p);
    }
  }
}

module Socket(h, pocket) {
  difference() {
    cylinder(d=socketOuterDiameter, h=h, $fn=60);
    translate([0,0,h-pocket])
      cylinder(d=socketInnerDiameter, h=height, $fn=60);
  }
}

// Arm is intersected with a ramped spiral to get the slope
module RampedSpiral() {
  // SpiralSlope
  ds = 0.5 + wallThickness/distance;
  for (socket=[0:numSocketsPerArm])
    SpiralSegment(socket, height-deltaH*(socket-ds));

  // First socket
  t = socketT(0);
  p = pointXY(t);
  translate(p)
    cylinder(d=socketOuterDiameter, h=height, $fn=60);
}

// The s:th segment of that spiral
module SpiralSegment(s, h) {
  eps = 0.001;
  t0 = socketT(s);
  t1 = socketT(s+1);
  steps=(s==1)? 40 : max(30-5*s, 10);
  dt = (t1-t0)/steps;
  dh = deltaH/steps;
  dl = distance/steps;

  for (i=[0:steps-1]) {
    t = t0 + i*dt;
    SpiralSlice(t-eps, t+dt+eps, h-i*dh);
  }
}

// Adds k*N(t) to the point (x(t), y(t)). N is unit-length normal to the curve.
function addNormal(t, k) = let (deg = t*degreesPerRadian,
                                 c = cos(deg), s=sin(deg),
                                 x = radius*(c + t*s),
                                 y = radius*(s - t*c))
                             [x + k*s, y - k*c];

// Points p1 -> p2 -> p3 are enumerated in counterclockwise order
function counterClockwise(p1, p2, p3) = let (dx1 = p2[0] - p1[0],
                                              dy1 = p2[1] - p1[1],
                                              dx2 = p3[0] - p2[0],
                                              dy2 = p3[1] - p2[1])
                                          (dx1*dy2 - dx2*dy1) > 0;

// Each SpiralSegment is interpolated by several "slices"
module SpiralSlice(t0, t1, h) {
  p0_prim = addNormal(t0, -0.5*socketOuterDiameter);
  p0_bis = addNormal(t0, +0.5*socketOuterDiameter);
  p1_prim = addNormal(t1, -0.5*socketOuterDiameter);
  p1_bis = addNormal(t1, +0.5*socketOuterDiameter);

  dx1 = p0_bis[0] - p0_prim[0];
  dy1 = p0_bis[1] - p0_prim[1];
  a = atan2(dy1, dx1);
  r = sqrt(p0_prim[1]*p0_prim[1] + p0_prim[0]*p0_prim[0]);
  andSome = 1;

  if (counterClockwise(p1_bis, p1_prim, p0_prim)) {
    difference() {
      linear_extrude(height=h)
        polygon([p0_prim, p0_bis, p1_bis, p1_prim]);

      translate(p0_prim)
        rotate(a)
          translate([-0.5*andSome, 0, h])
            rotate([-slope, 0])
              cube([r + socketOuterDiameter + andSome, socketOuterDiameter, deltaH]);
    }
  }
}

```


[User]
I'm looking for a way to generate custom FPV camera mounts for quadcopters, and I'd like to do it with OpenSCAD. I've got a few specific requirements and parameters I need to be able to control.

Essentially, I need to generate a mount that consists of two standoffs, with arms that connect to a lens ring that holds the camera lens. I need to be able to specify:

*   **Standoff Separation:** The distance between the centers of the two standoffs.
*   **Camera Width:** The width of the camera body at the mounting screw point.
*   **Lens Inner Diameter:** The inner diameter of the lens support ring where the lens goes.
*   **Lens Offset:** The distance from the center of the mounting screw to the back of the lens ring.
*   **Camera Inset:** How far forward or backward the camera sits relative to the standoffs. Positive values put the camera behind the standoffs, negative in front.
*   **Lens Ring Thickness:** The thickness of material around the lens.
*   **Standoff Outer Diameter:** The outer diameter of the standoffs.
*   **Standoff Grip Height:** How far up the standoffs the grips extend vertically.
*   **Standoff Grip Thickness:** How thick the material is on each side of the standoff.
*   **Camera Angle:** The desired angle of the camera's up-tilt.
*  **Lens Height:** The vertical length of the cylindrical portion of the lens ring.
*   **Arm Height:** The height of the arms where they connect to the lens ring.
*   **Arm Radius:** The radius of the external fillet on the lens ring arms.
*   **Screw Diameter:** The diameter of the screw hole.
*   **Screw Driver Diameter:** The diameter of the hole that allows access to the screw.

The design should include the following features:

*   **Standoffs:** Two cylinders that act as the base of the mount, with grips extending from them to connect to the main body of the mount.
*   **Standoff Grips:** Block-like structures that connect the standoffs to the camera mount arms.
*   **Lens Ring:** A circular structure that holds the camera lens, attached to the standoff grips by arms.
*   **Filleted Arms:** Smooth, curved transitions where the arms meet the lens ring.

I'd like the design to be parametric, so I can easily modify the various dimensions and angles.  I'd prefer to see this as a single, primary file to make it easier to use and share, but I'm open to other options if that makes more sense for the overall design. I'm not particular about libraries, but if you think something like BOSL would be helpful to incorporate for more complex functions like fillets, that would be fine. I do want to be sure I can generate a good-looking and printable STL output with smooth curves.

[Assistant]
fixed-angle-cam-mount-generator.scad:
```
// resolution of curves
$fs = 0.01;

// USER VARIABLES, please change

// centre to centre
standoffSeparation = 31;
// width of the camera body at the mounting screw point
cameraWidth = 19;
// outer diameter of the lens / inner diameter of the support
lensInnerDia = 12;
// distance from the mounting screw to the back of the lens ring
lensOffset = 7;
// how far the camera sits inside the frame, use negative values to sit forwards
cameraInset = 0;
// thickness of material around the lens
lensRingThickness = 2.4;

// outer diameter minus 0.2mm for a tight fit
standoffOuterDia = 4.8;
// how far up the standoffs the grips extend vertically
standoffGripHeight = 9;
// amount of material either side of the standoff
standoffGripThickness = 1.6;
// desired amount of uptilt
cameraAngle = 45;
// distance from the back of the lens ring to the front
lensHeight = 3.2;
// how wide the arms are where it meets the lens ring
armHeight = 6;
// external fillet on the lens ring arms
armRadius = 4;
// size of the screw hole
screwDia = 2;
// size of the hole to access the screw
screwDriverDia = 5;

// CALCULATED VARIABLES, do not change
standoffOuterRad = standoffOuterDia / 2;
standoffGripOuterRad = standoffOuterRad + standoffGripThickness;
standoffGripOuterDia = standoffGripOuterRad * 2;
standoffHeight = standoffGripHeight + 10; // can be any large number, only used for cutting holes
lensInnerRad = lensInnerDia /2;
lensOuterDia = lensInnerDia + (lensRingThickness * 2);
lensOuterRad = lensOuterDia / 2;
LensRingSupportLength = lensOffset + lensHeight; // TODO should be based on the lens offset
LensRingSupportWidth = cameraWidth / 2; // takes us towards the ring, overlap to be cutoff
screwRad = screwDia / 2;
screwDriverRad = screwDriverDia / 2;
screwDriverLength = standoffGripOuterDia * 2; // big enough to stick clear out of the side
standoffGripWidth = (standoffSeparation - cameraWidth) / 2; // width of the standoff blocks to reach the camera
standoffGripInset = cameraInset < 0 ? cameraInset : 0; // how far the standoff grips extend forward

module bevelOuter(outerRadius, length){
    outerDia = outerRadius*2;
    intersection() {
        translate([-outerDia,-outerDia,0]){
            cube([outerDia, outerDia, length]);
        }
        cylinder(length, outerRadius, outerRadius);
    }
}
module bevelInner(innerRadius, length){
    innerDia = innerRadius*2;
    difference() {
        translate([-innerRadius,-innerRadius,0]){
            cube([innerRadius, innerRadius, length]);
        }
        cylinder(length, innerRadius, innerRadius);
    }
}
module bevelBoth(overallSize, joinSize, extrude){
    differenceSize = overallSize-joinSize;
    difference(){
        cube([overallSize, overallSize, extrude]);
        translate([overallSize,overallSize,0]){
            bevelInner(overallSize, extrude);
        }
        translate([overallSize,overallSize,0]){
            bevelOuter(differenceSize, extrude);
        }
    }
}
module lensHolder(){
    difference(){
        union(){

            // LHS suport arm
            // moves it so the rotation point is around the screw hole
            translate([lensHeight * 2 + cameraWidth, -LensRingSupportLength, armHeight/2]){
                rotate([0,180,0]){
                    LensRingSupport(LensRingSupportLength, LensRingSupportWidth, armRadius, armHeight, lensHeight);
                }
            }

            // RHS suport arm
            // moves it so the rotation point is around the screw hole
            translate([0, -LensRingSupportLength, -armHeight/2]){
                LensRingSupport(LensRingSupportLength, LensRingSupportWidth, armRadius, armHeight, lensHeight);
            }

            // lens ring
            translate([(cameraWidth/2) + lensHeight,-(LensRingSupportLength - lensHeight),0]){
                rotate([90,0,0]){
                    // pipe(lensInnerDia, lensOuterDia, lensHeight);
                    cylinder(lensHeight, lensOuterRad, lensOuterRad);
                }
            }
        }

        translate([(cameraWidth/2) + lensHeight,-(LensRingSupportLength - lensHeight),0]){
            // lens hole cutter
            translate([0, 2, 0]){
                rotate([90,0,0]){
                    cylinder(lensHeight + 4, lensInnerRad, lensInnerRad);
                }
            }
        }
    }
}
module LensRingSupport(supportLength, supportWidth, bevelRadius, extrudeHeight, supportThickness){
    extendLength = supportLength - bevelRadius;
    extendWidth = supportWidth - bevelRadius;
    union(){
        // small support to lens ring
        translate([bevelRadius, 0, 0]){
            cube([extendWidth, supportThickness, extrudeHeight]);
        }
        // large support to arm grips
        translate([0, bevelRadius, 0]){
            cube([supportThickness, extendLength, extrudeHeight]);
        }
        translate([0, 0, 0]){
            bevelBoth(bevelRadius, supportThickness, extrudeHeight);
        }
    }
}

difference(){
    // keepers
    union(){
        // standoff grips
        translate([0,0,-standoffGripHeight/2]){
            cylinder(standoffGripHeight, standoffGripOuterRad, standoffGripOuterRad);
            translate([standoffSeparation,0,0]){
                cylinder(standoffGripHeight, standoffGripOuterRad, standoffGripOuterRad);
            }
        }
        // standoff grip blocks
        translate([0,0,-standoffGripHeight/2]){
            translate([0,-standoffGripOuterRad + standoffGripInset,0]){
                cube([standoffGripWidth, standoffGripOuterDia + abs(cameraInset), standoffGripHeight]);
            }
            translate([standoffSeparation - standoffGripWidth, -standoffGripOuterRad + standoffGripInset,0]){
                cube([standoffGripWidth, standoffGripOuterDia + abs(cameraInset), standoffGripHeight]);
            }
        }
        // lens ring holder
        translate([standoffGripWidth-lensHeight, cameraInset, 0]){
            rotate([-cameraAngle,0,0]){
                lensHolder();
            }
        }
    }
    // cutters
    union(){
        // standoffs
        translate([0,0,-standoffHeight/2]){
            cylinder(standoffHeight, standoffOuterRad, standoffOuterRad);
            translate([standoffSeparation,0,0]){
                cylinder(standoffHeight, standoffOuterRad, standoffOuterRad);
            }
        }
        // screw hole
        translate([-standoffSeparation/2, cameraInset, 0]){
            rotate([0,90,0]){
                cylinder(standoffSeparation*2, screwRad, screwRad);
            }
        }
        // screwdriver hole RH
        translate([standoffGripWidth - 1.2, cameraInset, 0]){
            rotate([0,-90,0]){
                cylinder(screwDriverLength, screwDriverRad, screwDriverRad);
            }
        }
        // screwdriver hole LH
        translate([standoffSeparation - (standoffGripWidth - 1.2), cameraInset, 0]){
            rotate([0,90,0]){
                cylinder(screwDriverLength, screwDriverRad, screwDriverRad);
            }
        }
    }

}

```


[User]
Okay, so I'm trying to design a modular blind wand using OpenSCAD, and I want it to be easily customizable for different window sizes. I have some basic parameters in mind from a previous attempt, but I'd like a much more robust and flexible design, and I'm starting from scratch. I'm also trying to stay within the law so that I don't violate any patents.

Here's what I'm thinking for the overall structure:

I'd like to design it so that the wand is created from two pieces: a handle and an endcap. The handle will have an attachment point that connects to the endcap. I would like to have a parameter to disable the modular design, in case I just want a single continuous piece.

Here are some key parameters:

*   **`wandLength`**: This is the total length of the wand in centimeters.
*   **`wandSides`**: This defines the number of sides of the polygon forming the wand's cross-section (e.g., 6 for a hexagon, 8 for an octagon).
*   **`wandDiameter`**: The diameter of the wand, in millimeters.
*   **`holeDiameter`**: The diameter of the hole in the attachment point, in millimeters. This will be used to connect the two modular pieces.
*   **`attachmentPadding`**: This will be used to reduce the attachment size on one side to aid in printing. It should be adjustable in millimeters.
*   **`nubRatio`**: A ratio that defines the size of the retention nubs that hold the handle and endcap together. These nubs will sit within the attachment point.
*   **`quality`**:  This will control the fineness of the curve detail for the design, I guess.
*   **`printError`**: This allows for some spacing between the parts to account for tolerances, I'd like it in millimeters.
*  **`object`**: This should be either "handle" or "endcap" to control which part is rendered.
*   **`isModular`**: A boolean that will toggle the modular design.

I'd like the attachment to have a kind of dovetail shape or beveled edge where the pieces join, to ensure that it's a tight fit. The attachment point should include a hole for a connecting nub and I want nubs to ensure that it stays securely in place.

I think the design should be fully parametric, so I can easily change any of these parameters and get the desired result.

I'm thinking I need to use the `hull` command to create a smooth, continuous surface around the attachments. I'd also like to include some way to control the amount of padding for the attachments, to make sure the printed parts fit together snugly. It would be great if you could include some comments in the code, too. I also want to include parameters that allow the user to switch the render quality for printing.

Could you generate the OpenSCAD code for this? I'd also like for it to not include any external libraries for now, as I'd like to understand it better first.

[Assistant]
blind_wand.scad:
```
/* [Wand Settings] */

// total wand length (cm)
wandLength = 22;

// number of sides on the wand
wandSides = 8;

// diameter of blind wand (mm)
wandDiameter = 10.2;

// attachment hole size (mm)
holeDiameter = 3;

// reduce the attachment size on one side to aid in printing
attachmentPadding = 1;

// attachment nub bulge ratio
nubRatio = .25;

/* [Print Settings] */

// smoother renders slower
quality = 8; //[2:Draft, 4:Medium, 8:Fine, 16:Ultra Fine]

// how much margin should there be between parts
printError = .25;

// what do you want to print
object = "handle"; // [endcap, handle]

// will you be attaching multiple objects?
isModular = true;

/* [Hidden] */

// unit conversions
cm = 10; // cm = 10mm

// print quality settings
$fa = 12 / quality;
$fs = 2 / quality;

// additional parameters
attachmentLength = 15;

wandHandleLength = wandLength * cm - attachmentLength;
wandRadius = wandDiameter / 2;
wandBaseLength = wandRadius * cos(180 / wandSides) * 2;
wandInternalAngle = (wandSides - 2) * 180 / wandSides;

attachmentWidth = 2 * wandRadius * sin(180 / wandSides);
attachmentRadius = wandRadius;
attachmentRotation = (180 - wandInternalAngle) / 2;

rotate([0, 90, 0]) {
    if (object == "handle") {
        handle();
    } else {
        endcap();
    };
};

module endcap() {
    rotate([0, 0, attachmentRotation])
        rotate([180, 0, 0])
            hull() {
                endcapDepth = 1;
                cylinder(r = wandRadius, h = endcapDepth, $fn = wandSides);
                cylinder(r = wandRadius - endcapDepth, h = endcapDepth * 2, $fn = wandSides);
            };
    attachmentPost();
}

module retentionNubs(separation) {
    // retention nubs
    for(side = [1, -1]) {
        scale([1, nubRatio, 1])
            translate([0, separation / (2 * nubRatio) * side])
                sphere(r = holeDiameter / 2);
    }
}

module handle() {
    difference() {
        union() {
            // handle
            rotate([0, 0, attachmentRotation])
                cylinder(h = wandHandleLength, r = wandRadius, $fn = wandSides);

            // attachmentPoint
            translate([0, 0, wandHandleLength])
                attachmentPost();

            // add endcap
            if (!isModular) {
                endcap();
            }
        }

        if (isModular) {
            rotate([0, 0, 180])
                attachmentPost(isGap = true);
        };
    };

    if (isModular) {
        translate([0, 0, attachmentLength - attachmentRadius])
            retentionNubs(attachmentWidth);
    };
}

module attachmentPost(isGap = false) {
    length = attachmentLength;
    width = !isGap ? attachmentWidth - printError * 2 : attachmentWidth;

    padding = !isModular
        ? 0
        : !isGap ? attachmentPadding + printError * 2 : attachmentPadding;

    center = length - attachmentRadius;

    difference() {
        color("blue") hull() {
            // tip
            translate([0, 0, center])
                rotate([0, attachmentRotation, 0])
                    rotate([90, 0, 0])
                        cylinder(h = width, r = wandRadius, center = true, $fn = 8);

            // post base
            translate([0, 0, .5])
                cube([wandBaseLength, width, 1], center = true);
        };

        if (!isGap) {
            // hole
            translate([0, 0, center])
                rotate([90, 0, 0])
                    cylinder(h = 30, r = (holeDiameter + printError) / 2, center = true);
        };

        translate([-(wandBaseLength - padding) / 2, 0, length / 2])
            color("red") cube([padding, attachmentWidth, length], center = true);

    }

    if (isModular) {
        translate([0, 0, holeDiameter / 2 + 1.5])
            retentionNubs(width);
    }
}

```


[User]
I'd like to design a step block for calibrating my 3D printer, similar to the ones I've seen online. It should have a series of steps, each with a different height, and each step needs to have its height engraved on the top. I need it to be fully parametric so I can easily adjust the dimensions and number of steps.

Specifically, I'd like to be able to define these parameters:
*   The height of the *first* (thinnest) step.
*   The increment in height between each step.
*   The total number of steps.
*   The width and length (X and Y dimensions) of each step.
*   The depth of the engraved text for the step height.

The steps should be arranged in a line, with the thinnest step at one end and the thickest at the other. The text indicating each step's height should be on top of the step.

I need the design to work like "StepBlock_v2.scad", which does *not* require that the last step height is a multiple of anything. Basically, I want to input the parameters I mentioned, including the number of steps directly, and it should calculate the final height for me.  I do not want it to be like "StepBlock_v1.scad", which requires the user to calculate the number of steps. Also, I don't need it to be a Thingiverse Customizer, so don't worry about that part. I'd also like for it to use the "Liberation Sans" font. I believe that's built into OpenSCAD, so no external libraries should be necessary.

Could you generate a parametric OpenSCAD file for this?

[Assistant]
StepBlock_v2.scad:
```
//The thinnist step (mm)
FirstStepZ = 0.5;
//Step thickness increments (mm)
StepIncrementZ = 0.5;
//Number of Steps
Steps = 6;
//Step Width (mm)
StepX = 12.0;
//Step Length (mm)
StepY = 12.0;
//depth of engraved text label (mm)
TextDepth =0.4;
LastStepZ = StepIncrementZ*Steps;

for ( z = [FirstStepZ:StepIncrementZ:LastStepZ]){
  translate([StepX*Steps*z/LastStepZ -StepX,0,0]){
    difference(){
      translate([0, -StepY/2, 0]){
        cube([StepX, StepY, z], center=false);
      }
      translate([StepX/2, 0, z-TextDepth]){
        linear_extrude(TextDepth+0.01){
          rotate([0, 0, -90]){
            text(str(z), font = "Liberation Sans", valign="center", halign="center", size=5);
          }
        }
      }
    }
  }
}

```
StepBlock_v1.scad:
```
//The thinnist step
FirstStepZ = 0.4;
//Step thickness increments
StepIncrementZ = 0.4;
//The thickest step.  This must be a multiple of StepIncrementZ + FirstStepZ
LastStepZ = 2.4;
//Step Width
StepX = 12.0;
//Step Length
StepY = 12.0;
//depth of engraved text label
TextDepth =0.4;

Steps = (LastStepZ/FirstStepZ);

for ( z = [FirstStepZ:StepIncrementZ:LastStepZ]){
  translate([StepX*Steps*z/LastStepZ -StepX,0,0]){
    difference(){
      translate([0, -StepY/2, 0]){
        cube([StepX, StepY, z], center=false);
      }
      translate([StepX/2, 0, z-TextDepth]){
        linear_extrude(TextDepth+0.01){
          rotate([0, 0, -90]){
            text(str(z), font = "Liberation Sans", valign="center", halign="center", size=5);
          }
        }
      }
    }
  }
}

```


[User]
I'd like to design a customizable trophy base using OpenSCAD. I need it to be parametric so I can adjust the size and text. The base should have a cylindrical column with a few stepped layers at the bottom, and a plaque extending outwards from the side of the column. I'd like to be able to control the height of the base, the diameter of the top platform of the column, the width of the steps, and the dimensions and extension of the plaque.

Specifically, the design should include the following features:

*   A cylindrical column with a flat top and stepped base.
*   A rectangular plaque extending outwards from the side of the column. The plaque should be attached at roughly the midpoint of the column's height.
*   The ability to add three lines of text to the plaque, each with its own font, size, and vertical offset. The text should be extruded from the surface of the plaque.

Here are the parameters I need to be able to adjust:

*   `trophy_h`: Total height of the trophy base.
*   `top_diam`: Diameter of the top platform of the cylindrical column.
*   `step_width`: Width and height of the steps at the base of the column.
*   `plaque_w`: Width of the plaque.
*   `plaque_h`: Height of the plaque.
*   `plaque_ext`: Distance the plaque extends from the front of the column.
*   `text_d`: Thickness of the extruded text on the plaque.
*   `text_1`, `text_2`, `text_3`: Strings for the three lines of text.
*   `text_size_1`, `text_size_2`, `text_size_3`: Font sizes for the three lines of text.
*   `text_font_1`, `text_font_2`, `text_font_3`: Font names for the three lines of text.
*  `text_nudge_1`, `text_nudge_2`, `text_nudge_3`: Vertical adjustment for each line of text.

I would like to have the option to render the following parts separately for multi-extruder printing:

*   The column.
*   The plaque with the text.
* A version of the plaque that can be printed as support material for the other parts.

Additionally, I need a preview option to allow fast, low resolution renders.

For colors, please include parameters for `base_color` and `plaque_color` for previewing.

I don't need any fancy libraries; I'm just expecting to use standard OpenSCAD functions.

Can you generate the OpenSCAD files for me?

[Assistant]
Trophy_Base.scad:
```
// Trophy Base 1.0
// Author: Alexander Smith
// Released: April 30, 2019.
// License: CC BY 4.0 <https://creativecommons.org/licenses/by/4.0/>
//
// This renders a trophy base with text.

// The variables at the top are formatted for the Thingiverse Customizer.

// Set this to false to incrase detail for the final render. Rendering will be much slower.
preview = true; // [true: "Low-res preview", false: "Slow, high-res final"]

// Part to render.
part = "preview1";   // [single:Single-extruder, preview1:v1 Preview, base:v1 Dual - Base, text:v1 Dual - Text, preview2:v2 Preview, column:v2 Dual - Base, plaque:v2 Dual - Plaque, support:'Print as support' model]

// Height of the trophy base.
trophy_h = 120;     // [0:1:300]

// Diameter of the top plate.
top_diam = 120;     // [0:1:300]

// Height and width of the steps.
step_width = 5;     // [0:0.5:10]

// Plaque width.
plaque_w = 110;     // [0:1:300]

// Plaque height.
plaque_h = 80;      // [0:1:300]

// Distance plaque extends from front of cylinder.
plaque_ext = 5;    // [0:0.5:20]

// Text extrusion
text_d = 2;         // [0:0.05:5]

// Top line of text.
text_1 = "World's";

// Middle line of text.
text_2 = "#1";

// Bottom line of text.
text_3 = "Person";

// Size of top line of text. Very small text won't print well.
text_size_1 = 15;   // [0:0.5:300]

// Size of middle line of text.
text_size_2 = 40;   // [0:0.5:300]

// Size of bottom line of text.
text_size_3 = 15;   // [0:0.5:300]

// Font for the top line of text. The Google Fonts should all work (fonts.google.com).
text_font_1 = "Gentium Basic";

// Font for the middle line of text.
text_font_2 = "Wendy One";

// Font for the bottom line of text.
text_font_3 = "Gentium Basic";

// Fine tune vertical position of top line of text.
text_nudge_1 = 0;   // [-10:0.1:10]

// Fine tune middle line of text.
text_nudge_2 = 0;   // [-10:0.1:10]

// Fine tune bottom line of text.
text_nudge_3 = 0;   // [-10:0.1:10]

/* [Preview Colours] */
// Base colour.
base_color = [0.74, 0.6, 0.42];

// Plaque & text colour.
plaque_color = [0.8, 0.8, 0.8];

/* [Hidden] */
$fa = preview ? 6 : 0.5;
eps = preview ? 1.0e-2 : 1.0e-3;

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

// Sum the first n items of vector v.
function add_first(v, n=0) =
    (n==0)
        ? 0.0
        : v[n-1] + add_first(v, n-1);

module column(trophy_h, top_diam, step_width) {
    r = top_diam/2;

    union() {
        cylinder(r=r, h=trophy_h);
        cylinder(r=r + step_width, h=trophy_h - step_width);

        cylinder(r=r + 2*step_width, h=2*step_width);
        cylinder(r=r + 3*step_width, h=step_width);
    }
}

module plaque_plate(trophy_h, top_diam, step_width, plaque_size, plaque_ext) {
    r = top_diam/2 + step_width + plaque_ext;
    w = plaque_size[0];
    h = plaque_size[1];

    dh = (trophy_h - 3*step_width) / 2 + 2*step_width;

    translate([0, -r, dh]) rotate([90, 0, 0])
        translate([-w/2, -h/2, -r])
            cube([w, h, r]);
}

module plaque_plate_support(trophy_h, top_diam, step_width, plaque_size, plaque_ext) {
    r = top_diam/2 + step_width + plaque_ext;
    w = plaque_size[0];
    h = plaque_size[1];

    dh = (trophy_h - 3*step_width) / 2 + 2*step_width;

    difference() {
        translate([0, -r, 0]) rotate([90, 0, 0])
            // We shrink the box by eps so that the difference comes otu clean.
            translate([-w/2, 0, -r] + [eps, eps, 0])
                cube([w, h, r] - eps*[2, 2, 1]);

        union() {
            column(trophy_h, top_diam, step_width);
            plaque_plate(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
        }
    }
}

module plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d, text_lines, text_sizes, text_nudge, text_fonts) {
    r = top_diam/2 + step_width + plaque_ext;
    h = plaque_size[1];
    dh = (trophy_h - 3*step_width) / 2 + 2*step_width;

    // Space out the text evenly.
    dt = (h - add_first(text_sizes, 3)) / 4;

    etch_depth = min(4, r/10);

    translate([0, -r, dh]) rotate([90, 0, 0])
        for (i = [0:2]) {   // 0:2
            if (len(text_lines[i]) > 0) {
                padding = (i+1)*dt;
                prev_sizes = add_first(text_sizes, i);
                v_shift = padding + prev_sizes + text_sizes[i]/2;
                translate([0, h/2 - v_shift + text_nudge[i], -etch_depth])
                    linear_extrude(etch_depth + text_d)
                        text(text=text_lines[i], size=text_sizes[i],
                             font=text_fonts[i], valign="center", halign="center");
            }
        }
}

module column_and_plaque(trophy_h, top_diam, step_width, plaque_size, plaque_ext) {
    column(trophy_h, top_diam, step_width);
    plaque_plate(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
}

/*
 * Render a trophy base.
 * trophy_y: total height of the base
 * top_diam: diameter of the top platform
 * step_width: width and height of the trophy steps
 * plaque_size: [width, height] of the plaque
 * plaque_ext: how far the plaque extends from the column (recommended: = 2*step_width)
 * text_d: how far the text extends out from the surface of the plaque
 * text_lines: three text strings. (If you only want one, use the middle one.)
 * text_nudge: vertical shift to apply to each line of text
 * text_sizes: size of each line of text
 * text_fonts: font names to use for each line
 * part: which part to render:
 *     "single": single-extruder: everything
 *     "base": dual-extruder v1: column and plaque
 *     "text": dual-extruder v2: text
 *     "column": dual-extruder v2: column
 *     "plaque": dual-extruder v2: plaque and text
 *     "preview1": color render of v1 (default)
 *     "preview2": color render of v2
 */
module trophy_base(
    trophy_h = 120,
    top_diam = 120,
    step_width = 5,
    plaque_size = [110, 80],
    plaque_ext = 5,
    text_d = 2,
    text_lines = ["OpenSCAD", chr(9786) /* smiley face emoji U+263A */, "Rocks!"],
    text_sizes = [12, 40, 12],
    text_nudge = [0, 0, 0],
    text_fonts = ["Liberation Serif", "Liberation Serif", "Liberation Serif"],
    part = "single",
    base_color = [0.74, 0.6, 0.42],
    plaque_color = [0.8, 0.8, 0.8])
{
    if (part == "preview1") {
        color(base_color) column_and_plaque(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
        color(plaque_color) plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d, text_lines, text_sizes, text_nudge, text_fonts);

    } else if (part == "preview2") {
        color(base_color) column(trophy_h, top_diam, step_width);
        color(plaque_color) {
            plaque_plate(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
            plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d, text_lines, text_sizes, text_nudge, text_fonts);
        }

    } else if (part == "base") {    // version 1
        color(base_color) difference() {
            column_and_plaque(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
            plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d + 1, text_lines, text_sizes, text_nudge, text_fonts);
        }

    } else if (part == "text") {    // version 1
        color(plaque_color) plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d, text_lines, text_sizes, text_nudge, text_fonts);

    } else if (part == "column") {   // version 2
        color(base_color) difference() {
            column(trophy_h, top_diam, step_width);
            union() {
                plaque_plate(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
                plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d, text_lines, text_sizes, text_nudge, text_fonts);
            }
        }

    } else if (part == "plaque") {  // version 2
        color(plaque_color) {
            plaque_plate(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
            plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d + 1, text_lines, text_sizes, text_nudge, text_fonts);
        }

    } else if (part == "support") {
        plaque_plate_support(trophy_h, top_diam, step_width, plaque_size, plaque_ext);

    } else {    // "single" or unknown
        column_and_plaque(trophy_h, top_diam, step_width, plaque_size, plaque_ext);
        plaque_text(trophy_h, top_diam, step_width, plaque_size, plaque_ext, text_d, text_lines, text_sizes, text_nudge, text_fonts);
    }
}

trophy_base(trophy_h, top_diam, step_width,
    plaque_size=[plaque_w, plaque_h],
    plaque_ext=plaque_ext,
    text_d=text_d,
    text_lines=[text_1, text_2, text_3],
    text_sizes=[text_size_1, text_size_2, text_size_3],
    text_nudge=[text_nudge_1, text_nudge_2, text_nudge_3],
    text_fonts=[text_font_1, text_font_2, text_font_3],
    part=part
);

```


[User]
Hi! I'm looking to design a customizable bubble wand and container, kind of like the one described in the Thingiverse post, but I need it to be parametric and more flexible.

I want the container to be cylindrical, with a base and a lid. The lid should fit snugly inside the top of the container. I'd like both to have a chamfer where they meet to ensure a good seal, and a fillet on the base of the container to reduce overhang during printing.

I need the text to be customizable, including the font family, font weight, size, and vertical position relative to the center of the lid. Also, the resolution of the text and the whole shape in general should be parametric.

Could you generate the necessary OpenSCAD files for this? I'm happy to get a few files if that makes sense for the project structure. I'd prefer to keep the text rendering separate from the overall structure, if that helps with organization.

Specifically, here are some of the parameters I need control over:

*   `container_radius`: Radius of the container cylinder.
*   `container_height`: Height of the container cylinder.
*   `lid_inset`: The depth the lid goes into the container.
*   `lid_height`: The height of the lid
*   `chamfer_angle`: Angle of the chamfer between the container and the lid.
*   `fillet_radius`: Radius of the fillet at the base of the container.
*   `text_string`: The actual text to be displayed.
*   `font_family`: The font family to use.
*   `font_weight`: Bold/italic/light
*   `font_size`: Size of the text.
*   `text_vertical_position`: Vertical offset of the text from the center of the lid.
*   `resolution`: Resolution of the text and whole design (using `$fn`).

I'm expecting the main file will use `linear_extrude()` for the text and `difference()` to get the final shape of the container with the text cut out.

I'm not using any libraries right now but feel free to utilize any common ones if it makes things easier, like MCAD, BOSL, or BOSL2. I'd rather avoid using NopSCADlib or BOLTS. Please ensure I know which libraries you're using.

Thanks!

[Assistant]
FlaskofBubbles.scad:
```
//Flask of Bubbles

//Your text
text = "FoB";
//Size of the font
size = 17;//[2:1:24]
//Text vertical position
position = 2;//[-10:1:10]

//Font. Type in the font name
font_family = "French Script MT";
// Font weight. bold/italic/light
font_weight = "bold";

//Resolution.
resolution = 100;//[10:10:100]

$fn=resolution;

difference () {
cylinder(h=2,r=18.8,center=true);
translate([-0.6,position,0]){
linear_extrude(height=2) {
text(
    text,
    font = str(font_family, ":style=", font_weight),
    size=size,
    halign="center",
    valign="center");
}
}
}

```


[User]
Hey, I'm looking to design a parametric camera mount for some small AIO FPV cameras, specifically for whoop-style drones. I'd like the mount to be adjustable for different camera models and tilt angles, and ideally, it should print in TPU or a similar flexible material. I've got a pretty specific idea in mind based on some existing designs, so I'll try to be thorough!

First, the basic structure will be a mount with a bottom that attaches to the drone frame and a top part that secures the camera. The mount bottom needs to have mounting holes at a certain spacing, with some padding around those holes for strength. The camera is held in place by a friction fit, so the top part needs to be sized appropriately for the selected camera.

Here are the parameters I think I'll need:

**General:**

*   `cam`: This will be a string to select which camera model is used ("tx06", "f7", "cyclops", or "tx03"). This will affect the camera dimensions.
*   `angle`: The tilt angle of the camera in degrees.

**Mount Bottom:**

*   `spacing`: The center-to-center distance between the mounting holes.
*   `screwHoleDiameter`: The diameter of the mounting holes.
*   `padding`: The amount of material around the mounting holes.
*   `mountHeight`: The height of the mount base.
*   `holeHeight`: The height of the mounting holes themselves (maybe this can be slightly more than the mount height).
*   `walls`: Thickness of the structural walls of the mount.

**Camera Settings:**

*   `camHeight`: The height of the camera body.
*   `camDepth`: The depth of the camera body.
*  `camLength`: The length of the camera body.
*   `camLensDiameter`: The diameter of the camera lens.
*   `camLensLength`: The length of the camera lens.
*   `camLensOffsetZ`: The vertical offset of the camera lens from the top of the camera body.
*   `camLensOffsetX`: The horizontal offset of the camera lens from the center of the camera body.
*   `antennaWidth`: The width of the camera's antenna.
*   `antennaDepth`: The depth of the camera's antenna
* `bottomHeightModifier`: An additional modifier for the overall bottom height.

**Mount Camera:**

*   `cutoutLeftHeight`: The height of the cutout on the left side of the camera mount.
*   `cutoutLeftZ`: The vertical position of the cutout on the left side.
*   `cutoutRightHeight`: The height of the cutout on the right side of the camera mount.
*   `cutoutRightZ`: The vertical position of the cutout on the right side.
*  `camLensCutoutWidth`: The width of the lens cutout.
*  `camLensCutoutHeight`: The height of the lens cutout.
* `cutoutDepth`: The depth of the cutouts.
* `roundness`: The amount to round the edges of the camera holder part.
*  `cutoutBigHeight`: The height of the large cutout at the back of the mount.
* `cutoutBigDepth`: The depth of the large cutout at the back of the mount.
* `sidesLength`: The overall length of the sides of the camera mount.

I'm also thinking about incorporating a small hook on the top of the camera mount as a sort of extra security measure (like a tiny zip-tie loop) but maybe that's a separate module. I'm not sure at this point.

For the cameras, the following dimensions change based on the selected `cam` variable:

*   **TX06:** Antenna is offset by the difference of the camera length and antenna width divided by two.
*  **Cyclops:** `camHeight` becomes 12.35, `antennaDepth` is 1, `camLensOffsetZ` is 0.4, `antennaOffset` is a negative value to position it further back, and `antennaWidth` is increased accordingly.
*   **TX03:** `camHeight` becomes 14.1, `camLength` becomes 19.6, `camLensOffsetZ` is 0.6, `camLensOffsetX` is 0.4, `antennaWidth` is 10.5, and the antenna offset is 6.

I think I will need to use the `hull()` function, and I am assuming that I will need to use `difference()` for the cutouts, and `translate()` and `rotate()` to position everything. I should also probably use the `module` feature to make code more reusable.

Could you generate the OpenSCAD code for this design? I'd like to keep all the calculations and camera-specific logic within the SCAD file itself; I'd prefer it if you could avoid relying on any external libraries if possible, but if you feel it would make the code significantly more clear or easier to work with, feel free to use well-known ones.

Thanks in advance!

[Assistant]
cam_mount.scad:
```
/* [General] */
// Camera you are using
cam = "f7"; // [tx06, f7, cyclops, tx03]
// Angle of the Cam mount
angle = 30;

/* [Mount Bottom] */
spacing = 26;
screwHoleDiameter = 1.9; // 2.1
padding = 1.2;
mountHeight = 1.2; //

holeHeight = 2;

walls = 1.2; // 1.4

/* [Cam settings] */
camHeight = 13.35; // 13.3
camDepth = 7.5; // 8
camLength = 14.45; // 14.4
camLensDiameter = 10.2; // 10
camLensLength = 7;
camLensOffsetZ = 0.8; // 1
camLensOffsetX = 0.7; // 1.4, 1.2, 1.0, 0.9

antennaWidth = 6;
antennaDepth = 3;

bottomHeightModifier = 0; // 2

/* [Mount cam] */
cutoutLeftHeight = 7;
cutoutLeftZ = 3.25;

cutoutRightHeight = 5;
cutoutRightZ = 0;

camLensCutoutWidth = 4;
camLensCutoutHeight = 10;

cutoutDepth = 0.6;

roundness = 2;

cutoutBigHeight = 10.5;
cutoutBigDepth = 2.5;

sidesLength = 13.3;

/*
hookLength = 2.5;
hookHeight = 2;
*/

/* [Hidden] */
$fn = 90;

// F7 3in1 is default
antennaOffset = 0;

// TX06
if(cam == "tx06") {
  antennaOffset = (camLength - antennaWidth) / 2;
  all(antennaOffset, antennaDepth, antennaWidth, camLength, camHeight, camLensOffsetX, camLensOffsetZ);
}

// Cyclops
else if(cam == "cyclops") {
  camHeight = 12.35;
  antennaDepth = 1;
  camLensOffsetZ = 0.4;
  antennaOffset = -walls - 1;
  antennaWidth = antennaWidth + walls + 1;
  all(antennaOffset, antennaDepth, antennaWidth, camLength, camHeight, camLensOffsetX, camLensOffsetZ);
}

// TX03
else if(cam == "tx03") {
  camHeight = 14.1;
  camLength = 19.6;
  //antennaDepth = 1;
  camLensOffsetZ = 0.6;
  camLensOffsetX= 0.4;
  antennaWidth = 10.5;
  antennaOffset = 6;
  echo((camLength - antennaWidth) / 2);
  all(antennaOffset, antennaDepth, antennaWidth, camLength, camHeight, camLensOffsetX, camLensOffsetZ);
}

else {
  all(antennaOffset, antennaDepth, antennaWidth, camLength, camHeight, camLensOffsetX, camLensOffsetZ);
}

module all(antennaOffset, antennaDepth, antennaWidth, camLength, camHeight, camLensOffsetX, camLensOffsetZ) {
  diagonal = sqrt(spacing * spacing + spacing * spacing);
  screwHoleRadius = screwHoleDiameter / 2;
  camLensRadius = camLensDiameter / 2;

  c = camDepth + walls;
  beta = 90 - angle;
  a = c * cos(beta);
  b = sqrt(c * c - a * a);

  translate([-camLength / 2 - walls - camLensOffsetX, -b + padding + screwHoleRadius, mountHeight + a])
    rotate([-angle, 0, 0])
      translate([0,0,-bottomHeightModifier])
        camHolder();
  mount();

  module mount() {
    difference() {
      group() {
        //Sides
        hull() {
          translate([-diagonal / 2, 0, 0])
            cylinder(h = mountHeight, r = screwHoleRadius + padding);
          translate([diagonal / 2, 0, 0])
            cylinder(h = mountHeight, r = screwHoleRadius + padding);

          translate([-1, -sidesLength + screwHoleRadius + padding, 0])
            cube([2, sidesLength, mountHeight]);
        }
        translate([-diagonal / 2, 0, 0])
          cylinder(h = holeHeight, r = screwHoleRadius + padding);
        translate([diagonal / 2, 0, 0])
          cylinder(h = holeHeight, r = screwHoleRadius + padding);

        // Front
        hull() {
          translate([0, -diagonal / 2, 0])
            cylinder(h = mountHeight, r = screwHoleRadius + padding);

          translate([-8, -5 + screwHoleRadius + padding, 0])
            cube([16, 2, mountHeight]);
        }
        translate([0, -diagonal / 2, 0])
          cylinder(h = holeHeight, r = screwHoleRadius + padding);

        // Enforcement (front)
        hull() {
          translate([-1, -b + padding + screwHoleRadius, 0])
            cube([2, 0.1, a / 3 * 2 + mountHeight]);

          translate([-1, -diagonal / 2 + screwHoleRadius + padding - 1, 0])
            cube([2, 0.1, holeHeight]);
        }

        //Enforcement left side
        hull() {
          translate([camLength / 2 + walls - camLensOffsetX - 0.1, screwHoleRadius + padding - b + 0.5, 0])
            cube([0.1, 2, a / 3 * 2 + mountHeight]);

          translate([diagonal / 2 - screwHoleRadius - padding + 1 - 0.1, -1, 0])
            cube([0.1, 2, holeHeight]);
        }

        //Enforcement right side
        hull() {
          translate([-camLength / 2 - walls - camLensOffsetX, screwHoleRadius + padding - b + 0.5, 0])
            cube([0.1, 2, a / 3 * 2 + mountHeight]);

          translate([-diagonal / 2 + screwHoleRadius + padding - 1, -1, 0])
            cube([0.1, 2, holeHeight]);
        }

        translate([-camLength / 2 - walls - camLensOffsetX, -b + padding + screwHoleRadius, 0]) {
          wedge();
        }
      }

      translate([-diagonal / 2, 0, -1])
        cylinder(h = mountHeight + 2, r = screwHoleRadius);
      translate([diagonal / 2, 0, -1])
        cylinder(h = mountHeight + 2, r = screwHoleRadius);
      translate([0, -diagonal / 2, -1])
        cylinder(h = mountHeight + 2, r = screwHoleRadius);
    }
  }

  module wedge() {
    difference() {
      group() {
        translate([-a/4, -a/4, mountHeight]) {
          cube([camLength + walls * 2 + a / 2, b + a/4, a/4]);
        }

        translate([0, 0, mountHeight])
          cube([camLength + walls * 2, b, a]);
      }

      translate([camLength + walls * 2 + a / 2 + 1, b, mountHeight])
          rotate([angle, 0, 180])
            cube([camLength + walls * 2 + a + 2, camDepth + walls + 1, a]);

      translate([-1 - a / 2, -a / 4, mountHeight + a / 4])
        rotate([0,90,0])
          cylinder(h = camLength + walls * 2 + a + 2, r = a / 4);

      translate([-a / 4, -a / 4 - 1, mountHeight + a / 4])
        rotate([0, 90, 90])
          cylinder(h = camLength + walls * 2 + 2, r = a / 4);

      translate([-a / 4 + camLength + walls * 2 + a / 2, -a / 4 - 1, mountHeight + a / 4])
        rotate([0, 90, 90])
          cylinder(h = camLength + walls * 2 + 2, r = a / 4);
    }
  }

  module camHolder() {
    // Hook top
    /*
    translate([camLength / 2 + walls - hookLength / 2, camDepth + walls, camHeight - hookHeight])
      cube([hookLength, walls, walls + hookHeight]);
    */
    difference() {
      group() {
        hull() {
          translate([0,0, bottomHeightModifier])
            cube([camLength + walls * 2, camDepth + walls, walls]);
          translate([roundness, 0, camHeight + walls - roundness])
            rotate([-90, 0, 0])
              cylinder(r = roundness, h = camDepth + walls);
          translate([camLength + walls * 2 - roundness, 0, camHeight + walls - roundness])
            rotate([-90, 0, 0])
              cylinder(r = roundness, h = camDepth + walls);
        }

        rotate([90, 0, 0])
          translate([camLength / 2 + walls + camLensOffsetX, camHeight / 2 + camLensOffsetZ, -walls])
            cylinder(h = camLensLength, r = camLensRadius + walls);
      }

      // Lens Cutout
      rotate([90, 0, 0])
        translate([camLength / 2 + camLensOffsetX + walls, camHeight / 2 + camLensOffsetZ, -walls - 1])
          cylinder(h = camLength + 2, r = camLensRadius);

      // Lens split
      for(i=[0:120:360])
        translate([camLength / 2 + walls + camLensOffsetX, -camLensLength, camHeight / 2 + camLensOffsetZ])
          rotate([0, i, 0])
            translate([-camLensCutoutWidth / 2, 0, 0])
              cube([camLensCutoutWidth, camLensLength, camLensCutoutHeight]);

      // Main body cutout
      translate([walls, walls, 0])
        cube([camLength, camDepth + 1, camHeight]);

      // Antenna cutout
      translate([walls + antennaOffset, walls + camDepth - antennaDepth, 0])
        cube([antennaWidth, camDepth + 1, camHeight + walls + 1]);

      // Cutout right
      translate([-1, walls + camDepth - cutoutDepth, cutoutRightZ])
        cube([walls + 2, cutoutDepth + 1, cutoutRightHeight]);

      // Cutout left
      translate([camLength + walls - 1, walls + camDepth - cutoutDepth, cutoutLeftZ])
        cube([walls + 2, cutoutDepth + 1, cutoutLeftHeight]);

      // Cutout Big
      translate([-1, walls + cutoutBigDepth, 0])
        cube([camLength + walls * 2 + 2, camDepth - cutoutBigDepth + 1, cutoutBigHeight]);
    }
  }
}
```


[User]
I need a parametric phone case for a Moto Z phone, specifically one that can handle the phone with or without the Moto power snap. It needs to be fully customizable with parameters for phone height, width, thickness, and the snap's thickness (which would be zero if the snap isn't being used).

The case should have a rounded, almost organic feel to it, so the corners should be smooth and not sharp. I'd also like a lip around the top edge of the screen to provide some protection, but not too big - just a subtle one.

I also need cutouts for the buttons on the side (power and volume), and also a cutout for the camera, and I will want these to be parametric as well.  The button and camera cutouts should go all the way through the case.

I'd like to specify the shell thickness, lip height, and the size of the corner rounding as parameters.  I would want to be able to specify the radius of the camera hole and the x/y positions of the button holes, also. It would be great to have a preview of it before generating the STL as well.

Please use OpenSCAD to generate this, and I'd prefer to have it structured such that if I import this case into another design I can easily adjust dimensions, etc. I'm not looking for any specific style beyond the rounded corners and a lip on top for screen protection. I'd also like this to be compatible with any generic Moto Z model as possible.

I would prefer to not need to use any external libraries like BOSL or MCAD.

[Assistant]
Moto_Z_Case.scad:
```
// [Dimensions]

// Phone Height
h=156.4;
// Phone Width
w=76.2;
// Phone Tickness
phoneTickness=6;
// Snap Tickness
snapTickness=4.99;

// [Hidden]
tickness=phoneTickness + snapTickness;
topBorder = 1.4;
topHeight = 1.5;
shell = 1.2;
cameraRad = 12;
caseW = w+2*shell;
caseH = h+2*shell;
caseT = tickness+shell;
$fn=40;

module baseShape(t = tickness, bottomRatio=0.95, topRatio=1) {
    d = 10;
    resize([w, h, t])
    hull() {
        translate([d, d, 0])
        cylinder(h=t, r1=d* bottomRatio, r2=d * topRatio);

        translate([d, h-2*d, 0])
        cylinder(h=t, r1=d* bottomRatio, r2=d * topRatio);

        translate([w-2*d, h-2*d, 0])
        cylinder(h=t, r1=d * bottomRatio, r2=d * topRatio);

        translate([w-2*d, d, 0])
        cylinder(h=t, r1=d * bottomRatio, r2=d * topRatio);
    }
}

module phone() {
    hull() {
        baseShape(t=snapTickness, bottomRatio=0.7);

        translate([0, 0, snapTickness])
        baseShape(t=phoneTickness, bottomRatio=1);
    }
}

module topBorder() {
    color("#0ABAB5")
    difference() {
        translate([0, 0, caseT])
        resize(newsize=[caseW, caseH, topHeight])
        baseShape(t=topHeight, bottomRatio=1);

        translate([topBorder/2, topBorder/2, caseT-1])
        resize(newsize=[caseW-topBorder, caseH-topBorder, 4])
        baseShape(t=4, bottomRatio=1, topRatio=0.75);
    }
}

color("#0ABAB5")
difference() {
    // Build the basic phone shell bounding box
    union() {
        resize(newsize=[caseW, caseH, caseT]) phone();
        topBorder();
    }

    // Cut the phone interior
    translate([shell, shell, shell+0.2]) phone();

    // Cut button holes
    translate([caseW-5, 100, snapTickness + shell + 0.2])
    cube([6, 36, phoneTickness+topHeight]);

    translate([28, -1, snapTickness + shell + 0.2])
    cube([35, 6, phoneTickness+topHeight]);

    // Cut camera hole
    translate([caseW/2, caseH-cameraRad-14, -1])
    cylinder(h = tickness, r1=cameraRad, r2=cameraRad);
};
```


[User]
I'd like to design a parametric ball bearing in OpenSCAD, and I'm aiming for something similar to the design I've seen in ball-bearing.scad, but I'd prefer it if the code was completely generated by you, so I can understand it better. I'd like it to be customizable using the Customizer.

The key parameters I need to control are:

*   **Resolution:** The smoothness of the curves.
*   **Shaft Radius:** The radius of the inner hole that fits on the shaft.
*   **Gap Radius:** The radius to the center of the ball bearings.
*   **Bearing Radius:** The radius of the outer part of the bearing.
*   **Ball Bearing Height:** The height of the bearing.
*   **Ball Radius:** The radius of the ball bearings themselves.
*   **Number of Balls:** How many balls are in the bearing.
*   **Ball Carrier Ring Radius:** The radius of the cage that keeps the balls in place.

The bearing design should have:

1.  Two main bearing shells, an inner and outer one, separated by a groove for the ball bearings.
2.  A hole for inserting the ball bearings.
3.  A cage that holds the ball bearings in place, rotating inside the ball bearing groove.

The ball bearings should be evenly spaced inside the groove.

It would be very helpful if you could also include comments explaining what's going on in the code. If you need to use any libraries, please specify them.

[Assistant]
ball-bearing.scad:
```
// Parameters
Resolution = 100;
Shaft_radius = 4;
Gap_radius = 7.2;
Bearing_radius = 11;
Ball_bearing_height = 7;
Ball_radius = 2.35; // Increase 0.2 tolerance on diameter
Number_of_balls = 7;
Ball_carrier_ring_radius = 1.6; // Attention! Sketch mentions a diameter. This is a radius.

// Generates ball bearing
ball_bearing(Shaft_radius,Gap_radius,Bearing_radius,Ball_bearing_height,Ball_radius,Number_of_balls,Ball_carrier_ring_radius,Resolution);

// Ball bearing module
module ball_bearing(SR,GR,BR,h,r,n,rt,fn) {
    $fn=fn;
    // Create bearing shells
    difference()
    {
        rotate_extrude(angle=360)
        {
            difference()
            {
                union()
                {
                    // Inner shell
                    translate([SR,0,0]) square([GR-SR-(r/2), h]);
                    // Outer shell
                    translate([GR+(r/2),0,0]) square([BR-GR-(r/2), h]);
                }
                // Ball bearing groove
                translate([GR, h/2, 0]) circle(r);
            }
        }
        // Ball insertion hole
        translate([GR,0,h/2]) cylinder(h/2+1,r,r);
    }
    // Function to obtain the position of ball bearings
    function 3d_circle(t) = [
        (GR*cos(t)),
        (GR*sin(t)),
        h/2
        ];
    // Matrix with the coordinates of all the balls
    shape_points = [ for(i = [0:$fn/n:$fn]) let (a=i*360/$fn) 3d_circle(a)];
    // Create bearing cage
    difference()
    {
        // Create cage ring
        rotate_extrude(angle=360) translate([GR-(rt/2),0,0]) square([rt, h/2 + 0.5*r]);
        // Create all the balls
        for(i = [0:len(shape_points)-1]) translate(shape_points[i]) sphere(r);
    }
}

```


[User]
I need an OpenSCAD design for a vacuum cleaner adapter, basically a tube that goes from one diameter to another. I want to be able to specify the inside diameter of the vacuum cleaner side (which will be a male connector), the outside diameter of the tool side (which will be a female connector), the total length of the adapter, the size of the center cone transition as a percentage of the total length, and the adapter wall thickness.

The design should consist of three cylinders: one for the tool side, one for the vacuum cleaner side, and a truncated cone in the middle connecting the two. The cone should transition between the diameters, and all parts should have a wall thickness specified by the user.

I would like to be able to adjust these parameters:

*   `Vacuum_diameter`:  Interior diameter of the vacuum cleaner side (male part).
*   `Tool_diameter`: Exterior diameter of the tool side (female part).
*   `Adapter_length`: Total length of the adapter.
*   `Center_size`:  The size of the cone as a percentage of the total length (0-1).
*   `Adapter_thickness`: The wall thickness of the adapter.

Please make sure the code uses good practices, and includes comments that explain what each section of the code does. I'd like to avoid hardcoding things as much as possible. I also don't have any libraries installed, so it will need to be vanilla OpenSCAD.

Could you provide the OpenSCAD file?

[Assistant]
Vacuum_Cleaner_Adapter.scad:
```

/* [General] */
// Vacuum cleaner side, interior diameter (male part)
Vacuum_diameter = 39;
// Tool side, exterior diameter (female part)
Tool_diameter = 29;
// Full adapter length
Adapter_length = 60;
// Cone size, in percent
Center_size = 0.1; // [0:0.01:1]
// Adapter thickness
Adapter_thickness = 2;

/* [Hidden] */
$fn=100;
Center_length = Adapter_length * Center_size;
Part_length = (Adapter_length - Center_length) /2;

union() {
difference() {
    cylinder (r=Tool_diameter/2+Adapter_thickness,h=Part_length);
    translate([0, 0, -0.1]) cylinder (r=Tool_diameter/2,h=Part_length+0.2);
}

translate([0, 0, Part_length-0.1]) difference() {
    cylinder (r1=Tool_diameter/2+Adapter_thickness, r2=Vacuum_diameter/2,h=Center_length+0.1);
    translate([0, 0, -0.1]) cylinder (r1=Tool_diameter/2,r2=Vacuum_diameter/2-Adapter_thickness,h=Center_length+0.3);
}

translate([0, 0, Part_length+Center_length-0.1]) difference() {
    cylinder (r=Vacuum_diameter/2,h=Part_length+0.1);
    translate([0, 0, -0.1]) cylinder (r=Vacuum_diameter/2-Adapter_thickness,h=Part_length+0.3);
}
}

```


[User]
I'm trying to make a design that would allow for a dual-material 3D printer to create a gradient filament, kind of like what Turbo_SunShine has done. My current OpenSCAD code is a mess, and it's way too slow, so I want to start fresh with something parametric. The basic idea is to create a series of torus sections, sort of like segments of a spiral, that alternate between two materials, and transition between them using a sinusoidal function.

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

1.  **Base Spiral Path:** The design should start with a spiral path, which is defined by a series of torus sections. The path itself will be a growing spiral in 2D, defined by a set of coordinates, radius, and angles. The spiral needs to move outwards, and a new segment should add on to the previous one. The segments are not always connected - they should be distinct torus segments.

2.  **Torus Sections:** Each segment along the spiral path is a torus section.  Each section is a portion of a torus created by extruding a circle along an arc. Each section is defined by a major radius, minor radius, start angle, and end angle.  The angles should be given as parameters. Each torus section should have a fixed cross-section (defined by a circle), which is extruded along an arc, as with `rotate_extrude`.

3. **Segment Separation:** Each of the torus section should be given a gap between one another.  This gap should be given by a parameter.

4.  **Two Materials:** I'm using a dual-extruder, so the design should alternate between two materials for each torus section.  One material is "top" and the other is "bottom".  Visually, the "top" material should be above the "bottom" material in the extrusion.

5.  **Material Gradient:** The crucial part is the gradient. For each segment, there should be a square-shaped cut-out that is applied to the "top" material, and is not applied to the "bottom" material.  This cut-out should be applied at the same position across the torus section. The size of the cut-out should be controlled by a sinusoidal function - the cut-out size for "top" should transition over time (as you progress through the spiral) between being almost non-existent to being almost the entire circle, and then back to non-existent. The "bottom" material should _not_ have the cut-out.

6.  **Parametric:** I'd like the following to be parameters:
    *   Number of torus sections (or some mechanism to control the length of the spiral).
    *   Major radius of the torus sections (though it can increase as the spiral grows).
    *   Minor radius of the torus sections (the diameter of the extruded circle).
    *   The overall width of the squares being cut-out
    *   Spacing between the end of one segment and the beginning of another.

7.  **Libraries:** I think this will involve `rotate_extrude`, so I don't think I need any external libraries.

In summary, I'm looking for a parametric OpenSCAD design that generates a spiral filament with two materials that blend together using a sinusoidal gradient, applied to the "top" material. This should be similar to what I have below, but I think it's easier to start over from scratch than to try to fix the code I already have.

```
/*
if you could get hob3l to work:
openscad -o archieA.csg -DA=1 -DB=0 archie.scad &
openscad -o archieB.csg -DA=0 -DB=1 archie.scad;
wait

time openscad -o archieA.stl -DA=1 -DB=0 archie.scad &
time openscad -o archieB.stl -DA=0 -DB=1 archie.scad;
wait
*/
function arcpt(r, theta) = [cos(theta), sin(theta)] * r;

// $fn = 8;

function archie(t, u=0, r=[]) =
    len(r) >= t ? r :
        let(c=[ [0, 0], [0, -1], [1,-1], [1, 0] ],
            ci = c[u%4],
            t0 = u * 90,
            dth = 90 / (u+1),
            rad = u+1)
        archie(t, u+1, concat(r,
            [for(j=[0:1:u]) [ci, rad, t0+j*dth, t0+(j+1)*dth]]));

function enumerate(l) =
    [for(i=[0:1:len(l)-1]) [i,l[i], i/(len(l)-1)]];

function dropfirstn(l, n) =
    [for(i=[n:1:len(l)-1]) l[i]];

module torussection1(maj, th0, th1, convexity=2) {
    rotate(th0-.01)
    rotate_extrude(angle=th1-th0+.02, convexity=convexity)
    translate([maj,0,0])
    children();
}

module torussection(maj, min, th0, th1, convexity=2) {
    torussection1(maj, th0, th1, convexity=convexity)
    circle(r=min);
}

module iord(condition) {
    if(condition) intersection() children();
    else difference() children();
}
module torussectionsegment(maj, min, th0, th1, frac=.5, rot=0, top=true, convexity=2) {
    torussection1(maj, th0, th1, convexity=convexity)
    if(top) {
        intersection() {
            rotate(360/16)
            circle(r=min, $fn=8);
            rotate(rot)
            translate([0,2*min + (frac-.5)])
            square(min*4, center=true);
        }
    } else {
        difference() {
            rotate(360/16)
            circle(r=min, $fn=8);
            rotate(rot)
            translate([0,2*min + (frac-.5)])
            square(min*4, center=true);
        }
    }
}

function density(t) = (cos(8*360*t) + 1)/2;

A = 1;
B = 1;

for(p = enumerate(dropfirstn(archie(144 * 8), 360))) {
    i = p[0];
    ci = p[1][0]*.6;
    rad = p[1][1]*.6;
    th0 = p[1][2];
    th1 = p[1][3];
    t = p[2];

    frac = density(t);

    if(A) {
        color("red")
        translate(ci)
        torussectionsegment(rad, 1.75/2, th0, th1, frac, 0, true);
    }
    if(B) {
        color("blue")
        translate(ci)
        torussectionsegment(rad, 1.75/2, th0, th1, frac, 0, top=false);
    }

    if(0)
    color(["red", "green", "blue", "yellow"][p[0]%4])
    translate(ci)
    torussection(rad, 1.75/2, th0, th1);
}
```

[Assistant]
archie.scad:
```
/*
if you could get hob3l to work:
openscad -o archieA.csg -DA=1 -DB=0 archie.scad &
openscad -o archieB.csg -DA=0 -DB=1 archie.scad;
wait

time openscad -o archieA.stl -DA=1 -DB=0 archie.scad &
time openscad -o archieB.stl -DA=0 -DB=1 archie.scad;
wait
*/
function arcpt(r, theta) = [cos(theta), sin(theta)] * r;

// $fn = 8;

function archie(t, u=0, r=[]) =
    len(r) >= t ? r :
        let(c=[ [0, 0], [0, -1], [1,-1], [1, 0] ],
            ci = c[u%4],
            t0 = u * 90,
            dth = 90 / (u+1),
            rad = u+1)
        archie(t, u+1, concat(r,
            [for(j=[0:1:u]) [ci, rad, t0+j*dth, t0+(j+1)*dth]]));

function enumerate(l) =
    [for(i=[0:1:len(l)-1]) [i,l[i], i/(len(l)-1)]];

function dropfirstn(l, n) =
    [for(i=[n:1:len(l)-1]) l[i]];

module torussection1(maj, th0, th1, convexity=2) {
    rotate(th0-.01)
    rotate_extrude(angle=th1-th0+.02, convexity=convexity)
    translate([maj,0,0])
    children();
}

module torussection(maj, min, th0, th1, convexity=2) {
    torussection1(maj, th0, th1, convexity=convexity)
    circle(r=min);
}

module iord(condition) {
    if(condition) intersection() children();
    else difference() children();
}
module torussectionsegment(maj, min, th0, th1, frac=.5, rot=0, top=true, convexity=2) {
    torussection1(maj, th0, th1, convexity=convexity)
    if(top) {
        intersection() {
            rotate(360/16)
            circle(r=min, $fn=8);
            rotate(rot)
            translate([0,2*min + (frac-.5)])
            square(min*4, center=true);
        }
    } else {
        difference() {
            rotate(360/16)
            circle(r=min, $fn=8);
            rotate(rot)
            translate([0,2*min + (frac-.5)])
            square(min*4, center=true);
        }
    }
}

function density(t) = (cos(8*360*t) + 1)/2;

A = 1;
B = 1;

for(p = enumerate(dropfirstn(archie(144 * 8), 360))) {
    i = p[0];
    ci = p[1][0]*.6;
    rad = p[1][1]*.6;
    th0 = p[1][2];
    th1 = p[1][3];
    t = p[2];

    frac = density(t);

    if(A) {
        color("red")
        translate(ci)
        torussectionsegment(rad, 1.75/2, th0, th1, frac, 0, true);
    }
    if(B) {
        color("blue")
        translate(ci)
        torussectionsegment(rad, 1.75/2, th0, th1, frac, 0, top=false);
    }

    if(0)
    color(["red", "green", "blue", "yellow"][p[0]%4])
    translate(ci)
    torussection(rad, 1.75/2, th0, th1);
}

```


[User]
I'm looking to create a divider for my DeWalt DW2190 Medium Tough Case, specifically designed to hold Forstner bits. I have a few particular requirements in mind, and I'd like the design to be parametric so I can easily tweak it.

The core of the divider will be a vertical wall, but the key feature is an integrated clasp to hold the shank of a Forstner bit. This clasp should be designed with a specific inner diameter to tightly grip the bit's shank. I also need to control the height of the shank axis above the base of the divider, as well as the clasp's thickness and width. I also need to be able to adjust the gap between the clasp and the main wall. Additionally, I need to control the thickness of the supporting floor beneath the clasp, and the outer wall thickness.

I'd like to be able to easily switch between imperial and metric units, so the design should be parametric and respect whatever units I choose.

To summarize the key parameters I need:

*   **Units:** (inches or millimeters)
*   **Shank Diameter:** The inner diameter of the clasp that holds the bit.
*   **Axis Height:** Height of the shank axis above the base of the divider.
*   **Clasp Thickness:** The thickness of the clasp material.
*   **Clasp Width:** The width of the clasp material.
*   **Gap Width:** The space between the clasp and the adjacent wall.
*   **Wall Width:** The width of the main wall.
*   **Floor Thickness:** The thickness of the supporting structure beneath the clasp.

Can you generate a set of OpenSCAD files that accomplish this? I don't need any additional functionality beyond that described - just the parametric divider with shank clasp for the DeWalt DW2190 case. I would prefer that the code be fairly readable and commented, if possible. Also, if you're going to use any external libraries, please let me know which ones.

[Assistant]
DeWalt_DW2190_div1.scad:
```
/***************************************************//**
 * Customizable DeWALT DW2190 Divider with Shank Clasp *
 * by Tobin Kyllingstad                                *
 * 2019/05/05                                          *
 *                                                     *
 * Divider for DeWalt Medium Tough Case (DW2190) with  *
 * integrated shank clasp.                             *
 * https://www.thingiverse.com/kyllingstad             *
 *******************************************************/
// (inches or millimeters)
Units = 1; // [1:in,25.4:mm]
// Shank Clasp Inner Diameter
Shank_Diameter = 0.32;
// Height of shank axis above base
Axis_Height = 0.5;
// Shank clasp thickness
Clasp_Thickness = 0.125;
// Shank clasp width
Clasp_Width = 0.125;
// Gap between clasp and wall
Gap_Width = 0.03125;
// Outer wall width
Wall_Width = 0.09375;
// Thickness of supporting structure
Floor_Thickness = 0.09375;
/* [Hidden] */
max_overhang_angle = 50;
margin = 0.001;
inner_radius = Shank_Diameter/2/Units;
axis_height = Axis_Height/Units;
clasp_thickness = Clasp_Thickness/Units;
clasp_width = Clasp_Width/Units;
gap_width = Gap_Width/Units;
wall_width = Wall_Width/Units;
floor_thickness = Floor_Thickness/Units;

scale([25.4, 25.4, 25.4])
{
   clasp_divider( inner_radius, axis_height, clasp_thickness, clasp_width,
                  gap_width, wall_width, floor_thickness );
}

module clasp_divider( inner_radius,
                      axis_height,
                      clasp_thickness,
                      clasp_width,
                      gap_width,
                      wall_width,
                      floor_thickness )
{
   div_height = 0.68;
   div_width = 1.2;
   div_depth = 0.75;
   hole_width = div_width - 0.375;
   floor_height = max( axis_height - inner_radius - clasp_thickness, floor_thickness );
   union()
   {
      difference()
      {
         divider( div_height, div_width, div_depth, hole_width, clasp_thickness, clasp_width,
                  floor_thickness, floor_height, gap_width, wall_width );
         slot( inner_radius, clasp_thickness/2, div_height - axis_height, div_depth, axis_height );
      }
      translate([ 0, 0, axis_height ])
      {
            clasp( inner_radius, clasp_thickness, axis_height - floor_thickness, clasp_width );
      }
   }
}

module divider( height,
                width,
                depth,
                hole_width,
                clasp_thickness,
                clasp_width,
                floor_thickness,
                floor_height,
                gap_width,
                wall_width )
{
   middle = clasp_width + 2*gap_width + 2*wall_width;
   hole_height = height - floor_thickness;
   hole_depth = clasp_width + 2*gap_width;
   difference()
   {
      divider_base( width, depth, middle, height );
      translate( [ 0, 0, floor_height + height/2 ] )
      {
         cube( size=[ hole_width, hole_depth, height ], center=true );
      }
      base_void( hole_width, hole_depth, floor_height - floor_thickness );
   }
}

module divider_base( width,
                     depth,
                     middle,
                     height )
{
   linear_extrude( height, convexity = 10 )
   {
      divider_profile( width, depth, middle );
   }
}

module divider_profile( width,
                        depth,
                        middle )
{
   polygon(points = [ [ width/2, depth/2],
                      [ width/2, 0.125],
                      [ (width/2 - 0.125), 0.1875],
                      [ (width/2 - 0.125), 0.0625],
                      [ width/2, 0.0625],
                      [ width/2,-0.0625],
                      [ (width/2 - 0.125),-0.0625],
                      [ (width/2 - 0.125),-0.1875],
                      [ width/2,-0.125],
                      [ width/2,-depth/2],
                      [ (width/2 + middle/2 - depth/2),-middle/2],
                      [-(width/2 + middle/2 - depth/2),-middle/2],
                      [-width/2,-depth/2],
                      [-width/2,-0.125],
                      [-(width/2 - 0.125),-0.1875],
                      [-(width/2 - 0.125),-0.0625],
                      [-width/2,-0.0625],
                      [-width/2, 0.0625],
                      [-(width/2 - 0.125), 0.0625],
                      [-(width/2 - 0.125), 0.1875],
                      [-width/2, 0.125],
                      [-width/2, depth/2],
                      [-(width/2 + middle/2 - depth/2), middle/2],
                      [ (width/2 + middle/2 - depth/2), middle/2] ]);
}

module slot( inner_radius,
             corner_radius,
             top_to_axis,
             depth,
             axis_height )
{
   translate([0, 0, axis_height])
   {
      rotate([90, 0, 0])
      {
         linear_extrude(height = depth, center = true, convexity = 10 )
         {
            slot_profile( inner_radius, corner_radius, top_to_axis );
         }
      }
   }
}

module slot_profile( inner_radius,
                     corner_radius,
                     top_to_axis )
{
   difference()
   {
      union()
      {
         circle(r = inner_radius, $fn = 50);
         polygon(points = [ [ inner_radius, 0],
                            [ inner_radius, top_to_axis - corner_radius],
                            [ (inner_radius + corner_radius), top_to_axis],
                            [ (inner_radius + corner_radius), top_to_axis + margin],
                            [-(inner_radius + corner_radius), top_to_axis + margin],
                            [-(inner_radius + corner_radius), top_to_axis],
                            [-inner_radius, top_to_axis - corner_radius],
                            [-inner_radius, 0] ]);
      }
      translate([ (inner_radius + corner_radius), top_to_axis - corner_radius])
      {
         circle(r = corner_radius, $fn = 20);
      }
      translate([-(inner_radius + corner_radius), top_to_axis - corner_radius])
      {
         circle(r = corner_radius, $fn = 20);
      }
   }
}

module base_void( width,
                  depth,
                  height )
{
   rotate([0, 0, 90])
   {
      rotate([90, 0, 0])
      {
         translate([0, 0, -width/2])
         {
            linear_extrude(height = width)
            {
               base_void_profile( depth, height );
            }
         }
      }
   }
}

module base_void_profile( depth,
                          height )
{
   polygon(points = [ [ 0, height ],
                      [  depth/2, height - tan(90 - max_overhang_angle) * depth/2 ],
                      [  depth/2, -margin ],
                      [ -depth/2, -margin ],
                      [ -depth/2, height - tan(90 - max_overhang_angle) * depth/2 ] ]);
}

module clasp( inner_radius,
              clasp_thickness,
              base_to_axis,
              clasp_width )
{
   rotate([90,0,0])
   {
      linear_extrude(height = clasp_width, center = true, convexity = 10)
      {
         clasp_profile( inner_radius, clasp_thickness, base_to_axis );
      }
   }
}

module clasp_profile( inner_radius,
                      clasp_thickness,
                      base_to_axis )
{
   outer_radius = inner_radius + clasp_thickness;
   opening_angle = 60;
   fillet_radius = ( min( base_to_axis, outer_radius ) -
                     outer_radius * cos(max_overhang_angle) ) /
                   ( 1 + cos(max_overhang_angle) );
   union()
   {
      difference()
      {
         union()
         {
            circle(r = outer_radius, $fn = 50);
            polygon(points = [ [ 0,0 ],                                         // center of clasp
               [ (outer_radius + fillet_radius) * sin(max_overhang_angle),
                -(outer_radius + fillet_radius) * cos(max_overhang_angle) ],    // center of fillet
               [ (outer_radius + fillet_radius) * sin(max_overhang_angle),
               -((outer_radius + fillet_radius) * cos(max_overhang_angle) +
                 fillet_radius + margin) ],                                     // bottom fillet
               [-(outer_radius + fillet_radius) * sin(max_overhang_angle),
               -((outer_radius + fillet_radius) * cos(max_overhang_angle) +
                 fillet_radius + margin) ],                                     // bottom fillet
               [-(outer_radius + fillet_radius) * sin(max_overhang_angle),
                -(outer_radius + fillet_radius) * cos(max_overhang_angle) ] ]); // center of fillet
         }
         circle(r = inner_radius, $fn = 50);
         translate([ (outer_radius + fillet_radius) * sin(max_overhang_angle),
                    -(outer_radius + fillet_radius) * cos(max_overhang_angle), ])
         {
            circle(r = fillet_radius, $fn = 50);
         }
         translate([-(outer_radius + fillet_radius) * sin(max_overhang_angle),
                    -(outer_radius + fillet_radius) * cos(max_overhang_angle), ])
         {
            circle(r = fillet_radius, $fn = 50);
         }
         polygon(points = [ [ 0,0 ],
            [ (outer_radius) * tan(opening_angle), (outer_radius) ],
            [-(outer_radius) * tan(opening_angle), (outer_radius) ] ]);
      }
      translate([ (inner_radius + clasp_thickness/2) * sin(opening_angle),
                  (inner_radius + clasp_thickness/2) * cos(opening_angle) ])
      {
         circle(d = clasp_thickness, $fn=20);
      }
      translate([-(inner_radius + clasp_thickness/2) * sin(opening_angle),
                  (inner_radius + clasp_thickness/2) * cos(opening_angle) ])
      {
         circle(d = clasp_thickness, $fn=20);
      }
   }
}

```


[User]
I'm looking to create a customizable divider for my DeWalt DW2190 medium tough case, specifically designed to hold Forstner bits. I'd like it to have two integrated shank clasps that will hold the bit shanks securely.

Here are the design parameters I'm thinking of:

*   **Units**: I need to be able to specify whether I'm working in inches or millimeters, and have all of the dimensions adjust accordingly.

*   **Shank Clasp Diameters:** I need separate inner diameters for each of the two clasps, as different bits have different shank sizes. Let's call these Shank_A_Diameter and Shank_B_Diameter.

*   **Axis Height:** The height of the shank axis above the base of the divider needs to be adjustable. This will be Axis_Height.

*   **Clasp Thickness:** The thickness of the material used to form the clasp. This should be Clasp_Thickness.

*   **Clasp Width:** The width of the clasp itself. This is Clasp_Width.

*   **Gap Width:** The space between the clasp and the main body of the divider. This is Gap_Width.

*   **Outer Wall Width:** The thickness of the walls forming the outer edge of the divider. We can call this Wall_Width.

*   **Floor Thickness:** The thickness of the supporting base of the divider. This is Floor_Thickness.

I imagine the divider as having a main body with cutouts for the clasps and a floor supporting them. I'd also like the clasp to have a rounded opening for easy insertion and removal of the bits.

I would really appreciate it if you could generate the OpenSCAD code for this, keeping all of these parameters adjustable through a customizer. Let me know if there's anything else you need from me.

[Assistant]
DeWalt_DW2190_div2.scad:
```
/********************************************************//**
 * Customizable DeWALT DW2190 Divider with Two Shank Clasps *
 * by Tobin Kyllingstad                                     *
 * 2019/05/05                                               *
 *                                                          *
 * Divider for DeWalt Medium Tough Case (DW2190) with two   *
 * integrated shank clasps.                                 *
 * https://www.thingiverse.com/kyllingstad                  *
 ************************************************************/
// (inches or millimeters)
Units = 1; // [1:in,25.4:mm]
// Shank Clasp A Inner Diameter
Shank_A_Diameter = 0.235;
// Shank Clasp B Inner Diameter
Shank_B_Diameter = 0.22;
// Height of shank axis above base
Axis_Height = 0.5;
// Shank clasp thickness
Clasp_Thickness = 0.08;
// Shank clasp width
Clasp_Width = 0.125;
// Gap between clasp and wall
Gap_Width = 0.03125;
// Outer wall width
Wall_Width = 0.09375;
// Thickness of supporting structure
Floor_Thickness = 0.09375;
/* [Hidden] */
max_overhang_angle = 50;
margin = 0.001;
a_inner_radius = Shank_A_Diameter/2/Units;
b_inner_radius = Shank_B_Diameter/2/Units;
axis_height = Axis_Height/Units;
clasp_thickness = Clasp_Thickness/Units;
clasp_width = Clasp_Width/Units;
gap_width = Gap_Width/Units;
wall_width = Wall_Width/Units;
floor_thickness = Floor_Thickness/Units;

scale([25.4, 25.4, 25.4])
{
   two_clasp_divider( a_inner_radius, b_inner_radius, axis_height, clasp_thickness, clasp_width,
                      gap_width, wall_width, floor_thickness );
}

module two_clasp_divider( a_inner_radius,
                          b_inner_radius,
                          axis_height,
                          clasp_thickness,
                          clasp_width,
                          gap_width,
                          wall_width,
                          floor_thickness )
{
   div_height = 0.68;
   div_width = 1.2;
   div_depth = 0.75;
   hole_width = div_width - 0.375;
   space = (hole_width - 2*a_inner_radius - 2*b_inner_radius - 4*clasp_thickness) / 3;
   a_offset = -(hole_width/2 - space - a_inner_radius - clasp_thickness);
   b_offset =  (hole_width/2 - space - b_inner_radius - clasp_thickness);
   min_inner_radius = min( a_inner_radius, b_inner_radius );
   floor_height = max( axis_height - min_inner_radius - clasp_thickness, floor_thickness );
   union()
   {
      difference()
      {
         divider( div_height, div_width, div_depth, hole_width, clasp_thickness, clasp_width,
                  floor_thickness, floor_height, gap_width, wall_width );
         translate([ a_offset, 0, 0 ])
         {
            slot( a_inner_radius, clasp_thickness/2, div_height - axis_height, div_depth, axis_height );
         }
         translate([ b_offset, 0, 0 ])
         {
            slot( b_inner_radius, clasp_thickness/2, div_height - axis_height, div_depth, axis_height );
         }
      }
      translate([ 0, 0, axis_height ])
      {
         translate([ a_offset, 0, 0 ])
         {
            clasp( a_inner_radius, clasp_thickness, axis_height - floor_height, clasp_width );
         }
         translate([ b_offset, 0, 0 ])
         {
            clasp( b_inner_radius, clasp_thickness, axis_height - floor_height, clasp_width );
         }
      }
   }
}

module divider( height,
                width,
                depth,
                hole_width,
                clasp_thickness,
                clasp_width,
                floor_thickness,
                floor_height,
                gap_width,
                wall_width )
{
   middle = clasp_width + 2*gap_width + 2*wall_width;
   hole_height = height - floor_thickness;
   hole_depth = clasp_width + 2*gap_width;
   difference()
   {
      divider_base( width, depth, middle, height );
      translate( [ 0, 0, floor_height + height/2 ] )
      {
         cube( size=[ hole_width, hole_depth, height ], center=true );
      }
      base_void( hole_width, hole_depth, floor_height - floor_thickness );
   }
}

module divider_base( width,
                     depth,
                     middle,
                     height )
{
   linear_extrude( height, convexity = 10 )
   {
      divider_profile( width, depth, middle );
   }
}

module divider_profile( width,
                        depth,
                        middle )
{
   polygon(points = [ [ width/2, depth/2],
                      [ width/2, 0.125],
                      [ (width/2 - 0.125), 0.1875],
                      [ (width/2 - 0.125), 0.0625],
                      [ width/2, 0.0625],
                      [ width/2,-0.0625],
                      [ (width/2 - 0.125),-0.0625],
                      [ (width/2 - 0.125),-0.1875],
                      [ width/2,-0.125],
                      [ width/2,-depth/2],
                      [ (width/2 + middle/2 - depth/2),-middle/2],
                      [-(width/2 + middle/2 - depth/2),-middle/2],
                      [-width/2,-depth/2],
                      [-width/2,-0.125],
                      [-(width/2 - 0.125),-0.1875],
                      [-(width/2 - 0.125),-0.0625],
                      [-width/2,-0.0625],
                      [-width/2, 0.0625],
                      [-(width/2 - 0.125), 0.0625],
                      [-(width/2 - 0.125), 0.1875],
                      [-width/2, 0.125],
                      [-width/2, depth/2],
                      [-(width/2 + middle/2 - depth/2), middle/2],
                      [ (width/2 + middle/2 - depth/2), middle/2] ]);
}

module slot( inner_radius,
             corner_radius,
             top_to_axis,
             depth,
             axis_height )
{
   translate([0, 0, axis_height])
   {
      rotate([90, 0, 0])
      {
         linear_extrude(height = depth, center = true, convexity = 10 )
         {
            slot_profile( inner_radius, corner_radius, top_to_axis );
         }
      }
   }
}

module slot_profile( inner_radius,
                     corner_radius,
                     top_to_axis )
{
   difference()
   {
      union()
      {
         circle(r = inner_radius, $fn = 50);
         polygon(points = [ [ inner_radius, 0],
                            [ inner_radius, top_to_axis - corner_radius],
                            [ (inner_radius + corner_radius), top_to_axis],
                            [ (inner_radius + corner_radius), top_to_axis + margin],
                            [-(inner_radius + corner_radius), top_to_axis + margin],
                            [-(inner_radius + corner_radius), top_to_axis],
                            [-inner_radius, top_to_axis - corner_radius],
                            [-inner_radius, 0] ]);
      }
      translate([ (inner_radius + corner_radius), top_to_axis - corner_radius])
      {
         circle(r = corner_radius, $fn = 20);
      }
      translate([-(inner_radius + corner_radius), top_to_axis - corner_radius])
      {
         circle(r = corner_radius, $fn = 20);
      }
   }
}

module base_void( width,
                  depth,
                  height )
{
   rotate([0, 0, 90])
   {
      rotate([90, 0, 0])
      {
         translate([0, 0, -width/2])
         {
            linear_extrude(height = width)
            {
               base_void_profile( depth, height );
            }
         }
      }
   }
}

module base_void_profile( depth,
                          height )
{
   polygon(points = [ [ 0, height ],
                      [  depth/2, height - tan(90 - max_overhang_angle) * depth/2 ],
                      [  depth/2, -margin ],
                      [ -depth/2, -margin ],
                      [ -depth/2, height - tan(90 - max_overhang_angle) * depth/2 ] ]);
}

module clasp( inner_radius,
              clasp_thickness,
              base_to_axis,
              clasp_width )
{
   rotate([90,0,0])
   {
      linear_extrude(height = clasp_width, center = true, convexity = 10)
      {
         clasp_profile( inner_radius, clasp_thickness, base_to_axis );
      }
   }
}

module clasp_profile( inner_radius,
                      clasp_thickness,
                      base_to_axis )
{
   outer_radius = inner_radius + clasp_thickness;
   opening_angle = 60;
   fillet_radius = ( min( base_to_axis, outer_radius ) -
                     outer_radius * cos(max_overhang_angle) ) /
                   ( 1 + cos(max_overhang_angle) );
   union()
   {
      difference()
      {
         union()
         {
            circle(r = outer_radius, $fn = 50);
            polygon(points = [ [ 0,0 ],                                         // center of clasp
               [ (outer_radius + fillet_radius) * sin(max_overhang_angle),
                -(outer_radius + fillet_radius) * cos(max_overhang_angle) ],    // center of fillet
               [ (outer_radius + fillet_radius) * sin(max_overhang_angle),
               -((outer_radius + fillet_radius) * cos(max_overhang_angle) +
                 fillet_radius + margin) ],                                     // bottom fillet
               [-(outer_radius + fillet_radius) * sin(max_overhang_angle),
               -((outer_radius + fillet_radius) * cos(max_overhang_angle) +
                 fillet_radius + margin) ],                                     // bottom fillet
               [-(outer_radius + fillet_radius) * sin(max_overhang_angle),
                -(outer_radius + fillet_radius) * cos(max_overhang_angle) ] ]); // center of fillet
         }
         circle(r = inner_radius, $fn = 50);
         translate([ (outer_radius + fillet_radius) * sin(max_overhang_angle),
                    -(outer_radius + fillet_radius) * cos(max_overhang_angle), ])
         {
            circle(r = fillet_radius, $fn = 50);
         }
         translate([-(outer_radius + fillet_radius) * sin(max_overhang_angle),
                    -(outer_radius + fillet_radius) * cos(max_overhang_angle), ])
         {
            circle(r = fillet_radius, $fn = 50);
         }
         polygon(points = [ [ 0,0 ],
            [ (outer_radius) * tan(opening_angle), (outer_radius) ],
            [-(outer_radius) * tan(opening_angle), (outer_radius) ] ]);
      }
      translate([ (inner_radius + clasp_thickness/2) * sin(opening_angle),
                  (inner_radius + clasp_thickness/2) * cos(opening_angle) ])
      {
         circle(d = clasp_thickness, $fn=20);
      }
      translate([-(inner_radius + clasp_thickness/2) * sin(opening_angle),
                  (inner_radius + clasp_thickness/2) * cos(opening_angle) ])
      {
         circle(d = clasp_thickness, $fn=20);
      }
   }
}

```