[User]
I need an OpenSCAD script to create a custom box around an imported STL file. I'd like the box to be generated in two halves, a top and a bottom, and I want to be able to adjust a few parameters:

*   **`model_height`:** The overall height of the box.
*   **`wall_thickness`:** The thickness of the box walls.
*   **`connector_height`:** The height of the connecting portion between the top and bottom box halves.
*   **`connector_thickness`:** The thickness of the connector (should be derived from wall_thickness, perhaps half the thickness).
*   **`stl_path`:** The path to the STL file I want to enclose.
*   **`view_buttom`:** A boolean to show or hide the bottom part of the box.
*   **`view_top`:** A boolean to show or hide the top part of the box.

The script should import the specified STL, scale it to the given `model_height`, and create hollow shells for the top and bottom box pieces. The connector should protrude from one of the halves, so they can fit together. I'm imagining that `connector_thickness` determines the clearance between the two parts. I need the box parts to be cut out in such a way that they can tightly surround the loaded .stl, but with the defined wall thickness all the way around the inside.

Can you generate this for me? Also I am not currently using any additional libraries.

[Assistant]
BoxCustomizer.scad:
```

$fn=10;

// height of the box
model_height = 55;

// wall thickness of the box
wall_thickness = 4;

// height of the connetor between top and buttom;
connector_height = 2;

// thickness of the connector;
connector_thickness = wall_thickness / 2;

//view buttom
view_buttom = true;

//view top
view_top = true;

//path to the stl what will be boxed
stl_path = "Lump_of_Coal_B.stl";

if (view_buttom){
union(){
connector(model_height, wall_thickness, connector_height, connector_thickness);
lump_part(model_height, wall_thickness, -1);
}}

if (view_buttom && view_top){
translate([0,0,connector_height*2])
top(model_height, wall_thickness, connector_height);
}else if(view_top){
    top(model_height, wall_thickness, connector_height);
}

module top(size, wall_thickness, connector_height)
{
    difference(){
    lump_part(size, wall_thickness, 1);
    mold(size, wall_thickness, connector_height);
    }
}

module lump(size){
    lump_path = stl_path;
    translate([0, 0, -size/2])
    resize([0,0,size], auto=true) import(lump_path);
}

module holow_lump(size, wall_size){
    difference()
    {
        lump(size);
        lump(size-(wall_size*2));
    }
}

module lump_part(size, wall_size, direction)
{
    difference()
    {
        holow_lump(size, wall_size);
        translate([0,0, -size*direction])cube(size*2, center = true);
    }
}

module connector(size, wall_size, height, thickness)
{
    linear_extrude(height)
    projection(cut = true)
    holow_lump(size-wall_size+thickness, thickness);
}

module mold(size, wall_size, height)
{
    linear_extrude(height)
    projection(cut = true)
    lump(size-wall_size);
}

```


[User]
Hey, I'm looking for some help designing a cookie box in OpenSCAD. I have a rough idea of what I want based on a design I found online, but I need it to be parametric so I can easily adjust the size and features.

Specifically, I'd like a rectangular box with rounded corners and a separate lid. The base of the box should have walls that are two layers thick when printed, and I'd like to achieve this using Minkowski sums to ensure smooth curves and consistent wall thickness. I'm hoping this will minimize travel during printing and improve print quality. I'm aiming for a lightweight design that doesn't waste too much filament since it's likely to be discarded after use.

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

*   **Box Length (LEN):** The length of the box.
*   **Box Width (WID):** The width of the box, ideally derived from the length using the golden ratio (WID = LEN / 1.618).
*   **Box Depth (DEP):** The height of the box.
*   **Corner Radius (R1):** The radius of the rounded corners.
*   **Wall Thickness (RW):** The thickness of the box walls.

The lid should fit snugly over the base. It should also have a lip that extends down into the box. I was thinking the lid should be a bit larger than the base to make sure that it fits easily.

Also, I'd like to add some simple decorative elements like a Christmas tree on the sides. I'd prefer something simple like a 2D outline of a tree.

For printing, I'm thinking:

*   Box: 3 top layers. No other special settings.
*   Lid: 2 bottom layers, zero top layers, 6 walls, 10% hex infill (for saving filament)

Could you help me create the OpenSCAD files for this box, making use of Minkowski sums for the walls, and using parameters for all of the dimensions? I'd like this to be in vanilla OpenSCAD, not using any external libraries. Thanks!

[Assistant]
ChristmasCookieBox.scad:
```
/* Christmas Cookie Gift Box - Lightweight

The box walls should print 2 walls thick with near-zero travel (PrusaSlicer).
This was achieved using minkowski-sphere on the overall shape for smoothness then minkowski-cylinder to achieve a uniform wall thickness.
Eliminating travel is good for print time and dramatically improved print quality for me.

I suggest 3 top layers for the bo, in general you shouldn't need to do anything special.

The lid can be printed with normal settings, or to save even more filament:
 - 2 bottom layers
 - Zero top layers
 - 6 walls
 - 10% Hex infill

 Christmas Cookie Gift Box - Lightweight by Daniel Urquhart https://www.thingiverse.com/dantu is marked with CC0 1.0 Universal. To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0
*/

//Box Length
LEN=120;
//Box Width
WID=LEN/1.618;
//Box Depth
DEP=35;

//Corner Radis
R1=5;
//wall thickness
RW = 0.85;

echo(LEN,WID,DEP);

//final box
translate([0,0,1])
difference(){
    minkowski(){
        smoothBox(1,0);

        translate([0,0,-0.01]) cylinder(h=0.01,r=RW,$fn=8);
    }

    smoothBox(0,1);
}

//lid
translate([0,WID+5])
{

    LE = 1.6;//extra size of lid (note smoothBox is 1mm larger than baseBox)
    linear_extrude(1) //very top of lid
        translate([R1,R1]) offset(r=R1+LE,$fn=32)square([LEN-R1*2,WID-R1*2]);

    linear_extrude(3) //lid lip
        translate([R1,R1]) difference(){
            offset(r=R1+(1-.4)   ,$fn=32) square([LEN-R1*2,WID-R1*2]);
            offset(r=R1-2 ,$fn=32) square([LEN-R1*2,WID-R1*2]);
    }

}

module smoothBox(ext_d=0,ext_h=0){
    minkowski(){
        baseBox(ext_d,ext_h);
        sphere(1,$fn=8);
    }

}

module baseBox(ext_d=0,ext_h=0){
translate([0,0,-ext_d]) linear_extrude(DEP+ext_h+ext_d)
    translate([R1,R1])offset(r=R1,$fn=32)square([LEN-R1*2,WID-R1*2]);

#translate([   0,  WID/2,0]) tree();
#translate([1+LEN,  WID/2,0]) tree();

//translate([10,-1,10])
//rotate([90,0,0]) linear_extrude(1) text("Merry Christmas",font="Segoe Script:style=Bold",size=8);

}
module tree(){
    rotate([0,-90,0]) linear_extrude(1) tree2d();
}
module tree2d(){
    translate([DEP/2,0,-2]){
        translate([ 9, 0])circle(d=DEP/5,$fn=3);
        translate([  5, 0])circle(d=DEP/4,$fn=3);
        translate([  0, 0])circle(d=DEP/3,$fn=3);
        translate([-6, 0])circle(d=DEP/2.2,$fn=3);
        translate([-10, 0])square(DEP/5,center=true);
    }
}

```


[User]
I need an OpenSCAD design for a parametric cable holder, and I'm looking for something similar to [this existing Thingiverse design](https://www.thingiverse.com/thing:6363359). I'd like it to be customizable with several parameters.

Here's what I have in mind:

**Core Structure:**

*   The basic unit should have a cylindrical core with a hole through the center for the cable.
*   On either side of the core, it should extend out with a flat, semi-circular shape (like a "D" shape).
*   There should be a slit running from the outer edge towards the center hole for easier cable insertion/removal. This slit should be rounded, not a sharp cut. Additionally, there should be side slits for routing the cable.
*   These units can be repeated to make a cable holder of various lengths.

**Parameters:**

*   `body_h`: Overall height of the cable holder.
*   `body_t`: Thickness of the "D" shaped extensions on the sides of the core.
*   `hole_d`: Diameter of the center hole for the cable.
*   `slit_w`: Width of the main cable slit.
*   `side_slit_w`: Width of the side slits.
*   `units`: Number of repeating units in the cable holder.
*   `spacing`: Spacing between the repeating units. This should be configurable so I can add gaps between units for mounting.
*   `slit_offset`: Vertical offset of the main slit, allowing it to be placed higher or lower on the holder.
*   `outside_screw_hole`: Diameter of optional screw holes at the ends of the holder. If set to zero, don't make these.
*   `screw_hole_type`: This should allow for different screw hole types, specifically "H" for a simple hole, and "C" for a hole with a countersunk.
*   `screw_hole_margin`: A margin around the screw holes.
*   `screw_overhung_type`: I'd like an option here for "O" for overhung holes, and "B" for bridge type holes.
*   `spacing_holes`: A list of diameters for screw holes located in the spacing between units.
*   `inside_holes`: A list of diameters for screw holes in the center of each unit.

**Additional Features:**
*   I want to include an option to carve some text on the side to indicate the size of parameters used. I'd call it `carve_size_signature`, a boolean flag.

**Other Configuration:**
*   The model should use a `max_overhung` value (in degrees) to determine how wide a 'bridge' is used.
*   There should be a `render_fn` and `preview_fn` to configure the $fn value for rendering and preview, respectively.
*   Internally, I want to have a `slit_resolution` variable for controlling the resolution of the rounded slit.

**Libraries:**

I don't expect you to use any external libraries, just vanilla OpenSCAD.

The design should be parametric and flexible, allowing easy adjustments for different cable sizes and mounting needs.

Could you generate the complete .scad file(s) for this?

[Assistant]
cable_holder.scad:
```
/**
 * Cable Holder
 *
 * Copyright (c) 2023, YAMAMOTO, N.
 * BSD 2-Clause License
 */

// Height
body_h = 15; // [1:30]

// Thickness
body_t = 3; // [0.5:0.5:10]

// Hole Diameter
hole_d = 8; // [1:0.1:20]

// Slit
slit_w = 5; // [1:0.1:20]

// Side Slit
side_slit_w = 5; // [1:0.1:20]

// Number of Units
units = 4; // [1:24]

/* [Details] */
// Spacing
spacing = 0; // [30]

// Slit Offset
slit_offset = 0; // [0:0.1:10]

/* [Screw] */
// Outsizde Screw Hole Diameter (experimental)
outside_screw_hole = 0; // [0:0.1:10]

// Screw Hole Type (experimental)
screw_hole_type = "C"; // [H: Hole, C:Hole with Countersunk]

// Screw Hole Margin (experimental)
screw_hole_margin = 1; // [0:0.5:10]

// Overhung Type (experimental)
screw_overhung_type = "B"; // [O: Overhung, B: Bridge]

// Spacing Screw Hole Diameters (experimental)
spacing_holes = [];

// Inside Screw Hole Diameters (experimental)
inside_holes = [];

/* [Misc] */
// Carve Size Signature
carve_size_signature = false;

// Screw Hole Max Overhung
max_overhung = 60;

// render $fn
render_fn = 32;

// preview $fn
preview_fn = 16;

/* [Hidden] */
// Slit Resolution
slit_resolution = 10;

$fn = $preview ? preview_fn : render_fn;
unit_w = hole_d + body_t * 2;
backplate_position = backplate_position(base = -unit_w / 2);

difference() {
  union() {
    for (pos = backplate_position) {
      translate([ pos[0] - unit_w / 2, 0, 0 ]) unit();
    }

    back_plate();
    outside_backplate();
  }

  screw_holes();

  if (carve_size_signature)
    size_signature();
}

function backplate_position(idx = 0, base = 0) =
    let(body = hole_d + body_t * 2,
        hole = (idx < len(spacing_holes))
                   ? (spacing_holes[idx] * (screw_hole_type == "C" ? 2 : 1))
                   : 0,
        plate_width = max(spacing, (hole > 0) ? hole + screw_hole_margin * 2
                                              : 0))(idx < units - 1)
        ? concat([[base + body, plate_width]],
                 backplate_position(idx + 1, base + body + plate_width))
        : [[base + body, 0]];

module screw_holes() {
  function orelse(v, els) = (v == undef) ? els : v;

  for (i = [0:len(backplate_position) - 1]) {
    pos = backplate_position[i];
    ih = orelse(inside_holes[i], 0);
    sh = orelse(spacing_holes[i], 0);

    if (ih > 0)
      translate([ pos[0] - unit_w / 2, 0, 0 ]) screw_hole_unit(ih);
    if (sh > 0)
      translate([ pos[0] + pos[1] / 2, 0, 0 ]) screw_hole_unit(sh);
  }
}

module screw_hole_unit(d) {
  translate([ 0, hole_d / 2, 0 ]) rotate([ -90, 0, 0 ]) {
    translate([ 0, 0, -hole_d / 2 ])
        cylinder(h = body_t + hole_d / 2 + 0.2, d = d);
    if (screw_overhung_type == "B") {
      w = cos(max_overhung) * d;
      translate([ -w / 2, -d / 2, 0 ]) cube([ w, d, body_t + 0.2 ]);
    }
    if (screw_hole_type == "C") {
      cylinder(h = d, d1 = d * 2, d2 = 0);
      translate([ 0, 0, -hole_d / 2 ])
          cylinder(h = hole_d / 2 + 0.01, d = d * 2);
    }
  }
}

module outside_backplate() if (outside_screw_hole > 0) {
  translate([ backplate_position[0][0] - unit_w, 0, 0 ]) rotate([ 0, 180, 0 ])
      outside_backplate_unit();
  translate([ backplate_position[len(backplate_position) - 1][0], 0, 0 ])
      outside_backplate_unit();
}

module outside_backplate_unit() {
  w = (screw_hole_type == "C" ? outside_screw_hole * 2 : outside_screw_hole) +
      screw_hole_margin * 2;
  fr = outside_screw_hole / 2;

  difference() {
    translate([ 0, hole_d / 2, body_h / 2 ]) rotate([ -90, 0, 0 ])
        linear_extrude(height = body_t) hull() {
      translate([ -0.1, 0, 0 ])
          square(size = [ w / 2 + 0.1, body_h ], center = false);
      translate(v = [ w - fr, fr ]) circle(r = fr);
      translate(v = [ w - fr, body_h - fr ]) circle(r = fr);
    }
    translate([ w / 2, 0, 0 ]) screw_hole_unit(outside_screw_hole);
  }
}

module size_signature() {
  h = (body_h - 1) / 3.0;
  translate([ -hole_d / 2 - body_t + 0.2, hole_d / 2 + body_t - 0.3, 0 ])
      rotate([ 90, 0, 180 ]) linear_extrude(height = 1) {
    translate([ 0, h * 0.5, 0 ])
        text(str(body_h, ",", body_t), size = h - 0.1, halign = "right");
    translate([ 0, -h * 0.5, 0 ])
        text(str(hole_d, ",", slit_w, ",", side_slit_w), size = h - 0.1,
             halign = "right");
    translate([ 0, -h * 1.5, 0 ])
        text(str(spacing, ",", slit_offset), size = h - 0.1, halign = "right");
  }
}

module back_plate() {
  translate([ -unit_w / 2, hole_d / 2, -body_h / 2 ]) cube(
      [ backplate_position[len(backplate_position) - 1][0], body_t, body_h ]);
}

module unit() {
  half_unit();
  rotate([ 0, 180, 0 ]) half_unit();
}

module half_unit() {
  difference() {
    base_shape();
    slit();
    side_slit();
  }
}

module side_slit() {
  // edge circle center X
  x = (hole_d + body_t) / 2 - side_slit_w;
  rad = x / (hole_d + body_t) * 2;

  difference() {
    translate([
      hole_d * (hole_d + body_t - 2 * side_slit_w) / (hole_d + body_t) / 2,
      -unit_w / 2 - 0.1, 0
    ]) cube([ unit_w / 2, unit_w / 2, body_h / 2 + 0.1 ]);
    for (z = [0.1:slit_w - 0.1:body_h / 2 + slit_w / 2])
      translate([ 0, 0.1, -z ]) slit();
    translate([ x, -sqrt(pow((hole_d + body_t) / 2, 2) - pow(x, 2)), 0 ])
        rotate([ 0, 0, asin(rad) ]) {
      cylinder(h = body_h, d = body_t + 0.01);
      translate([ -body_t, -body_t / 2, 0 ]) cube([ body_t, body_t, body_h ]);
    }
  }
}

module base_shape() {
  difference() {
    union() {
      cylinder(h = body_h / 2, d = hole_d + body_t * 2);
      translate([ -(hole_d / 2 + body_t), 0, 0 ])
          cube([ hole_d + body_t * 2, hole_d / 2 + body_t, body_h / 2 ]);
    }
    translate([ 0, 0, -1 ]) cylinder(h = body_h + 2, d = hole_d);
  }
}

module slit_base(i) {
  translate([ 0, 0, (body_h - slit_offset) / 2 * i / slit_resolution ])
      rotate(180 * i / (slit_resolution * 2), [ 0, 0, 1 ]) slit_base_unit();
}

module slit_base_unit() {
  rotate(90, [ 1, 0, 0 ]) cylinder(h = hole_d / 2 + body_t + 0.1, d = slit_w);
}

module slit() {
  for (i = [-slit_resolution:slit_resolution]) {
    hull() {
      slit_base(i);
      slit_base(i - 1);
    }
  }

  hull() {
    slit_base(slit_resolution);
    translate([ 0, 0, body_h / 2 ]) rotate([ 0, 0, 90 ]) slit_base_unit();
  }
}

```


[User]
Hey! I have this cool idea for a 3D printed "impossible table" repair, and I'd like to get some OpenSCAD files to make it. Basically, the design involves two identical tables, each with two of their legs "broken off" (really they're just short), and the tables are connected to each other using a bunch of strategically placed holes and some cord or rope.

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

*   **Two Tables:** The core design has two identical tables. I need to be able to control the following parameters for *both* tables at once:
    *   `length`: Length of the table top.
    *   `width`: Width of the table top.
    *   `thickness`: Thickness of the table top.
    *   `leglength`: Length of the *full* legs on each table (the ones that aren't broken).
    *   `brokenlength`: Length of the *broken* legs on each table.
    *   `legwidth`: Width of each leg (they are square in cross section).
    *   `holedia`: Diameter of the holes for connecting the tables with cord.
    *   `holedist`: Distance of the holes from the top of the legs (both the full and broken legs).

*   **Table Leg Design:** Each table will have two *full* legs and two *broken* legs. The full legs should extend down from the bottom of the table top by `leglength`, and the broken legs should extend down by `brokenlength`. The legs are essentially rectangular prisms with a small fillet along the edge.

*   **Holes:** Each table will have 4 holes total that are meant to be passed through by a cord to join two tables. The holes should extend through the legs with their center located `holedist` mm from the top of each leg. The holes on the full legs should be oriented in a 45 degree rotation on the XZ plane and the holes on the broken legs should be oriented in a 135 degree rotation on the XZ plane. The holes should be translated relative to their location and leg to extend fully through the leg and top.

*   **Mirroring/Rotation:** The idea is that once the first table is designed, we can rotate and flip that to make the second one, then connect them together using the holes. So each table should only be defined once, and there should be another set of files that put them in the correct final configuration for printing/viewing.

Could you generate a set of SCAD files for this? I don't need any external libraries for this. Also, please generate two files, one that defines the single table and its legs and holes, and the second that takes the single table definition and places two of them in a way that is ready for printing and visualization.

[Assistant]
ImpossibleTableRepair.scad:
```
// Impossible Table Repair
// Copyright 2022, 2024 MegaHertz67
// Released under the Creative Commons - Attribution license.
//
// Length of the table top (mm)
length = 55.0; // [20:0.1:300]
// Width of the table top (mm)
width = 55.0; // [20:0.1:300]
// Thickness of table top (mm)
thickness = 5.0; // [0.4:0.1:20]
// Length of the table leg (mm)
leglength = 40.0; // [15:0.1:300]
// Length of the broken leg (mm)
brokenlength = 4.5; // [2:0.1:20]
// Width of the table leg (mm)
legwidth = 5.0; // [2:0.1:30]
// Diameter of holes (mm)
holedia = 0.8; // [0:0.1:2]
// Distance of holes from top (mm)
holedist = 1.5; // [1:0.1:3]
$fn=20;
//
//Table top
linear_extrude(height=thickness) square([length, width], center=true);
difference() {
//full legs
    union(){
        translate([length/2-legwidth*1.02, width/2-legwidth*1.02, thickness])
        minkowski() {
            cube([legwidth*.96, legwidth*.96, leglength-.1], center=false);
            cylinder(h=.1, r=legwidth*.04);
        }
        translate([-length/2+legwidth*0.06, -width/2+legwidth*0.06, thickness])
        minkowski() {
            cube([legwidth*.96, legwidth*.96, leglength-.1], center=false);
            cylinder(h=.1, r=legwidth*.04);
        }
//broken legs
        translate([length/2-legwidth*1.02, -width/2+legwidth*0.06, thickness])
        minkowski() {
            cube([legwidth*.96, legwidth*.96, brokenlength-.1], center=false);
            cylinder(h=.1, r=legwidth*.04);
        }
        translate([-length/2+legwidth*0.06, width/2-legwidth*1.02, thickness])
        minkowski() {
            cube([legwidth*.96, legwidth*.96, brokenlength-.1], center=false);
            cylinder(h=.1, r=legwidth*.04);
        }
    }
//holes
    union() {
        translate([length/2, width/2, thickness+leglength-holedist]) rotate([90, 0, -45])
        linear_extrude(height=sqrt(length*length+width*width)) circle(d=holedia);
        translate([length/2, width/2, thickness+leglength-holedist-holedia*2]) rotate([90, 0, -45])
        linear_extrude(height=sqrt(length*length+width*width)) circle(d=holedia);
        translate([length/2, -width/2, thickness+brokenlength-holedist]) rotate([90, 0, -135])
        linear_extrude(height=sqrt(length*length+width*width)) circle(d=holedia);
        translate([length/2, -width/2, thickness+brokenlength-holedist-holedia*2]) rotate([90, 0, -135])
        linear_extrude(height=sqrt(length*length+width*width)) circle(d=holedia);
    }
}
```


[User]
I'd like to design a set of modular, removable organizer bins, similar to the ones you see in those plastic cases for small parts. I'm basing my design on the ones mentioned in the included description, particularly the Amazon Basics, Intertool, and Airtoon organizers.

The key thing is I need these bins to be parametric so I can customize their size and internal divisions. I want to be able to adjust:

*   **Overall dimensions:**  I need to specify how many "units" wide and long each bin is, with each unit being roughly 49mm. I should also be able to adjust the height of each bin, either as a multiple of the standard height or as an exact value in mm.
*   **Stacking:** The bins will be stackable, and I need to be able to specify if a bin is the top of the stack or not. The top bin needs a small lip to hold it on the organizer, but the others don't.
*   **Internal dividers:** I want to be able to control the number of subdivisions along the width and length of the bins, creating smaller compartments.
*   **Design details:**
    *   I need to be able to adjust the "lip depth" (how much the lip of the top bin extends inwards).
    *   I need to control the "corner radius" for rounded edges.
    *   I need to control the taper from top to bottom of the bin sides.
    *   I need to be able to specify the wall thickness.
    *   I need to control the height of the reinforced lip at the top of the bin.
    *   I need to control the thickness of the feet.

I would like for the model to use a `module` structure similar to the one in the included code. It would be ideal if the generated code did not rely on any external libraries, but just vanilla OpenSCAD, and could generate a single STL file with one or more bins based on the given parameters.

Finally, I'd like the code to create the feet separately so they can be mirrored and rotated around to each corner. This should give me better control over the design.

[Assistant]
Removable_Compartment_Organizer_Bins.scad:
```
/**
 *
 * https://www.amazon.com/dp/B08RZMKC9K - Amazon Basics 19-Removable Compartment Professional Organizer, Black, Orange, 12"L x 10.4"W x 2.3"H
 * https://www.amazon.com/dp/B09LSQSZ8J - INTERTOOL Multi-Purpose Small Parts Organizer, 24 Compartments, Portable Storage Carry Case
 * https://www.amazon.com/dp/B09WMCTF99 - AIRTOON Multi-Purpose Toolbox, Professional Organizer with 24 Bin Compartments
 * Addaped from:
 *     https://www.thingiverse.com/thing:2836113/
 *
 *

/** Customizer Support **/

/* [Box Size] */
// Number of units wide a box is (49mm Each).
box_width = 1; // [.5:1/2, 1:1, 1.5:1 1/2, 2:2, 2.5:2 1/2, 3:3,3.5:3 1/2,4:4]
// Number of units long a box is (49mm Each).
box_length = 1;// [.5:1/2, 1:1, 1.5:1 1/2, 2:2, 2.5:2 1/2, 3:3,3.5:3 1/2,4:4]

// Box Depth how tall the box is (for stacking).
box_depth = 1; // [1:1, 0.5:1/2, 0.33333:1/3, 0.25:1/4, 0.75:3/4, 0.66666:2/3]
// Is the box the top box in the stack?
top_of_stack = 1; // [1:Yes, 0:No]

// How many subdivisions along the width of the box.
subdivisions_wide = 1;
// How many subdivisions along the length of the box.
subdivisions_long = 1;

/* [Design Tweaks] */
//Single Unit Length Tested at 49.10
Unit_Length = 49.11;
//Single Unit Width Tested at 49.10
Unit_Width = 49.11;
// Lip Depth (Larger numbers will make the box smaller)
Lip_Depth = 1.01;
//Unit Hight (Tested at 50.65 with a 1.00 Foot Hight)
Unit_Height = 50.65;
// How Tall to Make the Feet (Adds to total Hight)
Foot_Height = 1.01;
// Corner Radius Tested at 3.5
Corner_Radius = 3.5;
// Box Taper Tested at 2.5
Taper = 2.5;
// Specify this based on your print settings.
Wall_Thickness = .82;
// The height of the wider reinforcing lip at the top of the box.
Lip_Height = 1.5;

// Make the Feet Thicker or Thiner(Should be at least Wall Thickness) Tested at 3 walls
Foot_Width = 1.22;
/* [Hidden] */
$fn = 64;

hf_tray(box_size = [box_width, box_length, box_depth],
            subdivisions = [subdivisions_wide, subdivisions_long],
            wall_thickness = Wall_Thickness,
            lip_height = Lip_Height,
            is_top = (top_of_stack == 1));

module hf_tray(box_size = [1, 1], subdivisions = [1, 1], wall_thickness = 0.8, lip_height = .75, is_top = true) {
    lip = Lip_Depth;
    corner_radius = Corner_Radius;
    taper = Taper;
    foot_height = Foot_Height;

    // Size of a 'small' unit in the storage system.
    unit_length = Unit_Length * box_size[0];
    unit_width = Unit_Width * box_size[1] ;
    unit_height = (Unit_Height * (len(box_size) < 3 ? 1 : box_size[2])) +
                         (len(box_size) < 3 || box_size[2] == 1 ? 0 : box_size[2] * (corner_radius + wall_thickness));

    x_div = subdivisions[0];
    y_div = subdivisions[1];

    // Do a bunch of boundary calculations up-front, so we don't have to keep doing math all over the place.

    // Top lip x and y
    top_lip_length = unit_length - (corner_radius * 2);
    top_lip_width = unit_width - (corner_radius * 2);

    // Top x and y
    top_length = top_lip_length - (lip * 2);
    top_width = top_lip_width - (lip * 2);

    top_inside_length = top_length - (wall_thickness * 2);
    top_inside_width = top_width - (wall_thickness * 2);

    // Bottom x and y
    bottom_length = top_length - (taper * 2);
    bottom_width = top_width - (taper * 2);

    bottom_inside_length = bottom_length - (wall_thickness * 2);
    bottom_inside_width = bottom_width - (wall_thickness * 2);

    // Flip it right-side up.
    translate([0, 0, unit_height + foot_height]) rotate([180, 0, 0])

    // Union everything.
    union() {
        // Subtract the 'inside' void'
        difference() {
            // Main shell.
            union () {
                // top lip.
                minkowski() {
                    translate([-top_lip_length / 2, -top_lip_width / 2, 0]) cube([top_lip_length, top_lip_width, lip_height / 2]);
                    cylinder(r = corner_radius, h = lip_height / 2);
                }

                // Taperd Body
                minkowski() {
                    // Hull the top and bottom for a proper taper.
                    hull() {
                        // top
                        translate([-top_length / 2, -top_width / 2, lip_height]) cube([top_length, top_width, 1]);
                        // bottom
                        translate([-bottom_length / 2, -bottom_width / 2, unit_height - corner_radius - 1]) cube([bottom_length, bottom_width, 1]);
                    }
                    // Minkowski a sphere around it.
                    sphere( r = corner_radius);
                }
            }
            // Cut the bottom of the body flush.
            // (removes minkowski round-over at the 'top' of the bin)
            translate([-unit_length / 2, -unit_width / 2, -corner_radius * 2]) cube([unit_length, unit_width, corner_radius * 2]);

            // Produce a 'positive' to remove from the main cavity. Think of it like a press die.
            difference() {
                // Tapered body main cavity.
                minkowski() {
                    // Hull the top and bottom for a proper taper.
                    hull() {
                        // top
                        translate([-top_length / 2, -top_width / 2, 0]) cube([top_length, top_width, 1]);
                        // bottom
                        translate([-bottom_length / 2, -bottom_width / 2, unit_height - corner_radius - wall_thickness - 1]) cube([bottom_length, bottom_width, 1]);
                    }
                    // Minkowski a sphere around it.
                    sphere( r = corner_radius - wall_thickness);
                }

                // Now that there's a positive shape for being pushed into the tray to form the inside,
                // remove any divider walls from the positive.
                if (x_div > 1 || y_div > 1) {
                    difference() {
                        union() {
                            if (x_div > 1) {
                                for ( xwall = [1 : x_div - 1] ) {
                                    translate([-top_length / 2 - (wall_thickness / 2) - corner_radius + (xwall * ((top_length + 2 * corner_radius) / x_div)),
                                                    -top_width / 2 - corner_radius,
                                                    0])
                                    if (is_top) {
                                        cube([wall_thickness, top_width + (2 * corner_radius), unit_height]);
                                    } else {
                                        translate([0, 0, foot_height + wall_thickness])
                                        cube([wall_thickness, top_width + (2 * corner_radius), unit_height]);
                                    }
                                }
                            }

                            if (y_div > 1) {
                                for (ywall = [1 : y_div - 1]) {
                                    translate([-top_inside_length / 2 - corner_radius,
                                                    -(top_inside_width / 2) - (wall_thickness / 2) - corner_radius + (ywall * ((top_inside_width + 2 * corner_radius) / y_div)),
                                                    0])
                                    if (is_top) {
                                        cube([top_inside_length + (2 * corner_radius), wall_thickness, unit_height]);
                                    } else {
                                        translate([0, 0, foot_height + wall_thickness])
                                        cube([top_inside_length + (2 * corner_radius), wall_thickness, unit_height]);
                                    }
                                }
                            }
                        }

                        // If we have divider walls, cut reliefs around the outside edge.
                        translate([-top_inside_length / 2 - corner_radius, -top_inside_width / 2 - corner_radius, 0])
                            rotate([-90, 0, 0])
                                cylinder(r = 3.5, h = top_inside_width + 2 * corner_radius);

                        translate([top_inside_length / 2 + corner_radius, -top_inside_width / 2 - corner_radius, 0])
                            rotate([-90, 0, 0])
                                cylinder(r = 3.5, h = top_inside_width + 2 * corner_radius);

                        translate([-top_inside_length / 2 - corner_radius, -top_inside_width / 2 - corner_radius, 0])
                            rotate([0, 90, 0])
                                cylinder(r = 3.5, h = top_inside_length + 2 * corner_radius);

                        translate([-top_inside_length / 2 - corner_radius, top_inside_width / 2 + corner_radius, 0])
                            rotate([0, 90, 0])
                                cylinder(r = 3.5, h = top_inside_length + 2 * corner_radius);
                    }
                }
            }
        }

        // Add the feet.
        foot(bottom_length = bottom_length, bottom_width = bottom_width, unit_height = unit_height, corner_radius = corner_radius, foot_height = foot_height);
        mirror([1, 0, 0]) foot(bottom_length = bottom_length, bottom_width = bottom_width, unit_height = unit_height, corner_radius = corner_radius, foot_height = foot_height);
        mirror([0, 1, 0]) foot(bottom_length = bottom_length, bottom_width = bottom_width, unit_height = unit_height, corner_radius = corner_radius, foot_height = foot_height);
        rotate([0, 0, 180]) foot(bottom_length = bottom_length, bottom_width = bottom_width, unit_height = unit_height, corner_radius = corner_radius, foot_height = foot_height);

    }
}

module foot(bottom_length = 0, bottom_width = 0, unit_height = 0, corner_radius = 2.5, foot_height = 2.5) {
    translate([-bottom_length / 2, -bottom_width / 2, unit_height - corner_radius]) {
        rotate([0, 0, 45])
        difference() {
            union() {
                //Add Center of Feet
                cylinder(r = corner_radius, h = foot_height + corner_radius);
                //Add Sides of Feet
                rotate([0, 0, -45])translate([0,-corner_radius,0])cube([corner_radius*.7,Foot_Width,foot_height+corner_radius]);
                rotate([0, 0, 45])translate([0,corner_radius-Foot_Width,0])cube([corner_radius*.7,Foot_Width,foot_height+corner_radius]);
            }
            //remove feet overlap Inside Bin
            translate([0, -corner_radius, 0])  cube([corner_radius*2, 2 * corner_radius, foot_height]);
            rotate([0,0,45])translate([0, -corner_radius*2, 0])  cube([corner_radius*2, 2 * corner_radius, corner_radius]);

            //remove inside overlap in bottom radius
            sphere(r = corner_radius);
            translate([corner_radius/2,-corner_radius/2,0])  sphere(r = corner_radius);
            translate([corner_radius/2,corner_radius/2,0])  sphere(r = corner_radius);

            //remove overlap from outside Bin in bottom radius
            translate([0,0,corner_radius])   cylinder(r = corner_radius-Foot_Width, h =foot_height);
            rotate([0, 0, -45]) translate([0,-corner_radius+Foot_Width,corner_radius]) cube([corner_radius*2, corner_radius*2, foot_height]);
            rotate([0, 0, -45]) translate([-corner_radius+Foot_Width,0,corner_radius]) cube([corner_radius*2, corner_radius*2, foot_height]);
            //Square feet off in bottom radius
            rotate([0, 0, -45]) translate([corner_radius*.7,-corner_radius,0]) cube([corner_radius , corner_radius*2, foot_height + corner_radius]);
            rotate([0, 0, -45]) translate([-corner_radius,corner_radius*.7,0]) cube([corner_radius*2, corner_radius, foot_height + corner_radius]);

        }

    }
}
```


[User]
I need a parametric OpenSCAD design for an XYZ corner bracket or joint. It should be a solid, cube-like object where each of the three visible faces (X, Y, and Z) have the same design and parameters. I've got some specific features in mind, so here's the breakdown:

**Core Structure:**

*   The basic shape is like a corner of a cube, so three faces meeting at a 90-degree angle, all with the same length. I need a parameter to control the overall `size` of each face.
*   Each face needs a consistent `width`. This is how thick the face will be.
*   Each face should have a circular `hole`. I need to be able to control the `hole` diameter.
*   The `hole` should be positioned away from the corner. I need to be able to control this `hole_offset` distance, or have it automatically offset to 40% of the `size` if I specify 0 for the offset.

**Optional Features:**

*   I'd like an optional `lip` along the two edges of each face that meet at the outside corner. I want to be able to control the size of the `lip` (how much it extends from the face) and set it to 0 to disable it.
*   I also need to be able to apply labels ("X", "Y", and "Z" respectively) to the faces. I want parameters for:
    *   The `label_width` which can be positive to extrude or negative to intrude (set to 0 to disable)
    *   The `label_size` for the text.
    *   And finally, the `label_offset` which controls how far it should be from the corner where the cube meets. I think this offset should be calculated from the label size (size-2).

**General Requirements:**

*   Everything should be fully parametric.
*   The default values for each parameter should be reasonable for testing, something like in the initial example I attached, i.e. size=20, hole=10, width=2, lip=0, label_width=-0.2, label_size=5, and hole_offset=0.
*   I'd prefer a smooth look, so I'd like the `$fn` to be configurable.
*   I'm okay with this being built from scratch, so you don't need to worry about updating existing files.
*   I would like the text to be centered on the area and rotated for X, Y, and Z in such a way that it is readable from an isometric view.
*   I would also like the label to be centered to the width of the side, so I do not have to worry about the exact placement of the label.

I think that should do it, I really appreciate the help.

[Assistant]
xyz-parametric.scad:
```
size = 20;
// 0 to off
hole = 10; // 0.01
// 0 is automatic
hole_offset = 0; // 0.01
width = 2; // 0.01
// 0 to off
lip = 0; // 0.01

/*  [Labels] */
font="Arial:style=Bold";
// negative intrude, positive extrude, 0 to off
label_width = -0.2;
label_size = 5;
label_X = "X";
label_Y = "Y";
label_Z = "Z";

/* [Options] */
$epsilon = 0.01;
$fn = 64;

label_offset = label_size-2;

module lip() {
    translate([0, 0, lip]) rotate([0, 90, 0]) linear_extrude(size) polygon([[lip,lip], [0, lip], [lip, 0]]);
}

module one(label, rot=0) {
    difference() {
        union() {
            cube(size=[size, size, width], center=false);
            if (lip > 0) {
                translate([0, size-lip, width]) lip();
                translate([size-lip, size, width]) rotate(-90) lip();
                //translate([size-lip, 0, width]) cube(size=[lip, size, lip], center=false);
            }
        }
        rotate(-45) translate([-size, size, -$epsilon]) cube(size=[2*size, size, width+lip+2*$epsilon], center=false);
        if (hole > 0) {
            ho = hole_offset > 0 ? hole_offset : size*0.4;
            translate([ho, ho, width/2]) cylinder(d=hole, h=width+2*$epsilon, center=true);
        }
        if (label != "" && label_width < 0 ) {
            translate([size-label_offset, label_offset+width, width+label_width+$epsilon]) rotate(rot) linear_extrude(height = -label_width)
                text(label, font=font, size=label_size, valign="center", halign="center");
        }
    }
    if (label != "" && label_width > 0 ) {
        translate([size-label_offset, label_offset, width]) rotate(rot) linear_extrude(height = label_width)
            text(label, font=font, size=label_size, valign="center", halign="center");
    }
}

rotate(180) union() {
    one(label_X, 180);
    rotate([-90, -90, 0]) one(label_Z, -90);
    rotate([90, 0, 90]) one(label_Y);
}

```


[User]
Hey, I'd like to create a customizable key chain using OpenSCAD. I have some specific ideas for the features, and it would be great if you could generate the code.

Here’s what I’m thinking:

**Overall Shape:** The base of the key chain should be a relatively simple shape that surrounds the name.

**Text:**
*   The key chain should include text.
*   The user should be able to customize this text and font.
*   The text should be raised from the base, and the user should be able to control its height.
*   The text should have a border, also of customizable height.
*   There should be an offset between the text and the text border, that is also customizable.
*   The user should have some control over text size and spacing, and the key chain shape should adjust accordingly.
*   The text should also be customizable in color.

**Border:**
*   There should be an outer border surrounding the text and base of the key chain.
*   The user should be able to customize the width and height of this border.
*   The user should also have control over the color of the border.

**Lug/Hole:**
*   There should be an optional lug for attaching the key chain to a keyring or whatever.
*   The user should be able to toggle this feature on or off.
*   The lug should include a hole.
*   The position of the text relative to the lug should be customizable.

**Color:**
*   The user should be able to set different colors for the base, text, and border.
*   There should be some option to export the design in multiple colors, but also to be able to see it as only a single color for visualization.

**Parameters:**

I imagine the user will need to be able to set parameters such as:

*   `TEXT`: the text displayed on the key chain
*   `Letter_Size`: the size of the letters in the text.
*   `Border`: toggle the outer border
*   `Border_Height`: the height of the outer border.
*   `Border_Width`: the width of the outer border
*   `Base_Height`: height of the base of the keychain
*   `Letter_Height`: the height of the text letters.
*   `Text_Border_Offset`: the offset between text and text border.
*    `Lug`: toggle the lug.
*   `Lug_Offset`: the position of the text relative to the lug.
*   `Inner_Margin`: a margin value between the base and the text.
*   `Outer_Margin`: A margin value between the base and the outer border
*   `FONT`: the font to be used for the text
*   `Base_Color`: the color of the base
*   `Name_Color`: the color of the text
*   `Border_Color`: the color of the outer border.
*    `export_Color`: option to see a single color for visualization or all colors.

Also, it would be awesome if you could incorporate some error handling for incorrect input values.

I think that's all for now.  Let me know if you have any questions!

[Assistant]
keyChain.scad:
```
TEXT = "Key Chain";

Letter_Size = 12; //[8:48]

$fn = 100;

Border = 1;// [0:false, 1:true]
Border_Height = 0.55;
Border_Width = 1.15;

Base_Height = 1.65;

Letter_Height = 1.05;

Text_Border_Offset = 1.05;

Lug = 1;// [0:false, 1:true]
Lug_Offset = 4.55;

Inner_Margin = 1.85;
Outer_Margin = 1.15;

FONT = "Bilbo Swash Caps"; //[Bangers,Bilbo Swash Caps,Bubblegum Sans,Cookie,Euphoria Script,Grand Hotel,Great Vibes,Lobster, Magneto,Mouse Memoirs,Pacifico,Pattaya,Rochester,Vag Rounded]

Base_Color = "white"; //[ blue, gray, white, yellow, orange, pink]
Name_Color = "white"; //[ blue, gray, white, yellow, orange, pink]
Border_Color = "blue"; //[ blue, gray, white, yellow, orange, pink]

// https://erik.nygren.org/2018-3dprint-multicolor-openscad.html
/* Pick a color below for STL export, or "ALL" to show all colors. */
export_Color = "ALL"; //[ ALL, blue, gray, white, yellow, orange, pink]

if (Lug) {
    multicolor(Base_Color)
    linear_extrude(Base_Height)
    difference(){
        union(){
        baseName();
        plain_Base();
        }
        lug_Hole();
    }
} else {
    multicolor(Base_Color)
    linear_extrude(Base_Height)
    baseName();
}

multicolor(Name_Color)
translate([0,0,Base_Height+Border_Height])
linear_extrude(Letter_Height)
name();

text_Border();

module text_Border () {
    multicolor(Border_Color)
    translate([0,0,Base_Height])
    linear_extrude(Border_Height)
    offset(Text_Border_Offset)
    name();
}

module lug_Hole(){
    translate([0.5,Letter_Size/5,0])
    offset(Letter_Size/12)
    square([Letter_Size/5,Letter_Size/3]/*[0.25,2.5]*/,center = true);
}

module plain_Base(){
    echo(len(TEXT));
    translate ([0,Letter_Size/8,0])
    offset(Letter_Size/8)
    square([len(TEXT)*Letter_Size/2.4,Letter_Size/6],center = false);
}

module baseName(){
  offset(Inner_Margin + Letter_Size/5){
    if (Lug) {
        plain_Base();
    }
    name();
  }
}
if ( Border ) {
  multicolor(Border_Color)
  translate([0,0,Base_Height])
  linear_extrude(Border_Height)
  border();
}

module border(){
  difference(){
    offset(-Outer_Margin){
      baseName();
    }
    offset(-Outer_Margin-Border_Width){
      baseName();
    }

  }
}

module name(){
    translate([Lug_Offset,Letter_Size/4,0])
    text(TEXT,font = FONT, size = Letter_Size, valign = "center", halign="left");
}

/* Similar to the color function, but can be used for generating multi-color models for printing.
 * The global current_color variable indicates the color to print.
 */
module multicolor(color) {
    if (export_Color != "ALL" && export_Color != color) {
        // ignore our children.
        // (I originally used "scale([0,0,0])" which also works but isn't needed.)
        //scale([0,0,0])
    } else {
        color(color)
        children();
    }
}
```


[User]
Okay, I'm looking to create a 3D-printed "flip text" object, like those where the text changes depending on the viewing angle. I have two names I want to use and some specific functionality. I'd like this to be created as a parametric OpenSCAD file.

Here's the rundown:

*   **Two Names:** The design needs to accept two text inputs (strings), let's call them `Name1` and `Name2`.
*   **Case:** Both names should be converted to uppercase.
*   **Fillers:** If the two names are different lengths, the shorter one should be "filled up" with a special character at the end.
*   **Special Character:** Spaces within either name should *also* be replaced by this special character. I need a way to choose between a few different special characters that will act as fillers -  a full heart, a hollow heart, a diamond, and a frame.
*   **Font:** The text should be rendered in a bold version of Arial font. The special character can use the same font, but maybe slightly larger.
*   **3D Effect:** Each letter should be extruded to create a 3D effect.
*   **Angle:** One name should be extruded at a 45-degree angle in one direction, the other name should be extruded at -45 degrees. These two extruded names should intersect each other.
*   **Base Plate:** All of this should sit on a base plate. I'm thinking a rectangular plate with rounded ends.
*   **Parameters:** I'd like to be able to set the text height, the spacing between letters, the extrusion depth, the plate thickness, and the different fill character options.
*   **Automatic centering/spacing:** If one name is shorter than the other, I'd also like it if filler characters were automatically added _before_ the name as well as after, to keep the text centered on the base plate.

Can you generate OpenSCAD code that does all of that? I'd like all the settings to be easily adjustable via parameters at the beginning of the file.

[Assistant]
Flip_Text.scad:
```
//V2.1
//change only these 2 lines
/*[Text]*/

Name1="Stella";
Name2="Chris";
//if one name is shorter than the other one, it will be filled up with hearts at the end
//lower chars will be replaced by upper chars
//spaces will be replaced by hearts
//some special chars will give strange effects
//---------------------
//Examples:
//name1="Dani";
//name2="Berni";
//name1="Alexandra";
//name2="Sebastian";
//name1="S t e l l a";
//name2=" C h r i s ";
//---------------------
/*[Hidden]*/
$fn=100;
heart=chr(9829);
text1=char_replace(strtoupper(Name1)," ",heart);
text2=char_replace(strtoupper(Name2)," ",heart);

text_height=50; //in mm
font="Arial:style=Bold";
font_special_char="Arial:style=Bold";
letter_space=text_height*1.3;
extrude_length=text_height*5;
number_of_chars=max(len(text1),len(text2));
special_char_multiply=1.3;
special_char_move=text_height/11;

plate_height=5;
plate_length=(number_of_chars-1)*letter_space;
plate_width=text_height*1.5;

render(){ //without that, fast preview is not working
    union(){
        translate([plate_length/2,0,plate_height/2]){
            cube([plate_length, plate_width, plate_height], center=true);
        }
        translate([0,0,plate_height/2]){
            cylinder(h=plate_height, d=plate_width, center=true);
        }
        translate([plate_length,0,plate_height/2]){
            cylinder(h=plate_height, d=plate_width, center=true);
        }

        for (i=[0:number_of_chars-1]){
            translate([letter_space*i,0,plate_height]){
                intersection(){
                    rotate([90,0,-45]){
                        translate([0,0,-extrude_length/2]){
                            linear_extrude(extrude_length){
                                if ((len(text1)<=i)||(text1[i]==heart)){
                                    translate([0,-special_char_move,0]){
                                        text(heart,size=text_height*special_char_multiply,font=font_special_char,halign="center");
                                    }
                                }
                                else{
                                    text(text1[i],size=text_height,font=font,halign="center");
                                }
                            }
                        }
                    }
                    rotate([90,0,45]){
                        translate([0,0,-extrude_length/2]){
                            linear_extrude(extrude_length){
                                if ((len(text2)<=i)||(text2[i]==heart)){
                                    translate([0,-special_char_move,0]){
                                        text(heart,size=text_height*special_char_multiply,font=font_special_char,halign="center");
                                    }
                                }
                                else{
                                    text(text2[i],size=text_height,font=font,halign="center");
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
//upper case for all chars
function strtoupper (string) =
  chr([for(s=string) let(c=ord(s)) c<=122 && c>=97 ?c-32:c]);

//-- Replace char(not string) in a string
function char_replace (s,old,new) =
  chr([for(i=[0:len(s)-1]) s[i]==old?ord(new):ord(s[i])]);
```
Flip_Text_-_V3.1.scad:
```
//V3.1
//by_dagobert_ch
//mod for different fillers and idea for fillup before and after name with fillers by phildc
//---------------------------------------
//change only these 3 lines
/*[Text]*/
Name1="Stella";
Name2="Chris";
Filler="heart2"; // ["heart", "heart1","heart2","diamond","frame"]
//if one name is shorter than the other one, it will be filled up with Filler at the end
//lower chars will be replaced by upper chars
//spaces will be replaced by Filler
//if filler name does not match, Filler "hearts2" will be used
//some special chars will give strange effects
//---------------------
//Examples:
//Name1="Dani";
//Name2="Berni";
//Name1="Alexandra";
//Name2="Sebastian";
//Name1="S t e l l a";
//Name2=" C h r i s ";
//Name1="Jean Francois";
//Name2="Chantal";
//---------------------
/*[Hidden]*/
$fn=100;

text1=strtoupper(Name1);
text2=strtoupper(Name2);

heart=chr(9829);

text_height=50; //in mm
font="Arial:style=Bold";
letter_space=text_height*1.3;
extrude_length=text_height*5;

number_of_chars=max(len(text1),len(text2));

//move both texts, so there are special chars before and after the text
move_text1=max(floor((len(text2)-len(text1))/2),0);
move_text2=max(floor((len(text1)-len(text2))/2),0);

font_special_char="Arial:style=Bold";
special_char_multiply=1.3;
special_char_move=text_height/11;

plate_height=5;
plate_length=(number_of_chars-1)*letter_space;
plate_width=text_height*1.5;

if(len(Name1)>0 && len(Name2)>0){

    render(){ //without that, fast preview is not working
        union(){
            translate([plate_length/2,0,plate_height/2]){
                cube([plate_length, plate_width, plate_height], center=true);
            }
            translate([0,0,plate_height/2]){
                cylinder(h=plate_height, d=plate_width, center=true);
            }
            translate([plate_length,0,plate_height/2]){
                cylinder(h=plate_height, d=plate_width, center=true);
            }

            for (i=[0:number_of_chars-1]){
                translate([letter_space*i,0,plate_height]){
                    intersection(){
                        rotate([90,0,-45]){
                            translate([0,0,-extrude_length/2]){
                                linear_extrude(extrude_length){
                                    if ((i<move_text1)||(len(text1)<=(i-move_text1))||(text1[i+move_text1]==" ")){
                                        translate([0,text_height/2-special_char_move,0]){
                                            difference(){  //remove the bottom of the heart, which goes under the plate
                                                special_char(text_height);
                                                translate([0,-text_height+special_char_move,0])
                                                    square([text_height,text_height],center=true);
                                            }
                                        }
                                    }
                                    else{
                                        text(text1[i-move_text1],size=text_height,font=font,halign="center");
                                    }
                                }
                            }
                        }
                        rotate([90,0,45]){
                            translate([0,0,-extrude_length/2]){
                                linear_extrude(extrude_length){
                                    if ((i<move_text2)||(len(text2)<=(i-move_text2))||(text2[i+move_text2]==" ")){
                                        translate([0,text_height/2-special_char_move,0]){
                                            difference(){  //remove the bottom of the heart, which goes under the plate
                                                special_char(text_height);
                                                translate([0,-text_height+special_char_move,0])
                                                    square([text_height,text_height],center=true);
                                            }
                                        }

                                    }
                                    else{
                                        text(text2[i-move_text2],size=text_height,font=font,halign="center");
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
//upper case for all chars
function strtoupper (string) =
  chr([for(s=string) let(c=ord(s)) c<=122 && c>=97 ?c-32:c]);

//Fillers by phildc
module special_char(n){
  if(Filler=="heart" ) translate([0,-text_height/2,0]) text(heart,size=text_height*special_char_multiply,font=font_special_char,halign="center");
  else if(Filler=="heart1" ) heart2(n,1);
  else if(Filler=="heart2") heart2(n,2);
  else if(Filler=="diamond") diamond(n,1);
  else if(Filler=="frame") diamond(n,2);
  else heart2(n,2);

} // mod special_char

module heart2(size,t){
    difference(){
        union(){
            r=0.7;
            translate([0,-size*r])
            scale([0.9*r,1.15*r])
            rotate([0,0,45]){
                square(size);
                translate([size/2,size])
                  circle(size/2,$fn=$fn);
                translate([size,size/2])
                  circle(size/2,$fn=$fn);
            } // rot
        } // uni

        if(t==2){
          union(){
            r=0.48;
            translate([0,-size*r])
                scale([0.9*r,1.15*r])
                    rotate([0,0,45]){
                        square(size);
                        translate([size/2,size])
                          circle(size/2,$fn=$fn);
                        translate([size,size/2])
                          circle(size/2,$fn=$fn);
                    } // rot
          } //uni
        } // if t 2
    } // diff
} // mod heart2

module diamond(s,t){
    size=s/1.555;
    if(t==1){
        translate([0,-s/2.2])  { // s/2
            rotate([0,0,45])
                difference() {
                    square(size*1.2);
                    translate([size/4,size/4])
                        square(size/1.6);
                }
                translate([-size/2.3/2,0])
                    square([size/2.3,size/5]);
        }  // tran
    } // t 1
    else{             // frame = t2
        translate([-size*1.5/2,-s/2.2])  { // s/2
            difference() {
                square(size*1.50);
                translate([size/4,size/4])
                    square(size);
            }

        }   // tran

    }  // t 2
} // mod diamond
```


[User]
I need a customizable drawer organizer designed in OpenSCAD. I have a specific drawer I want to fit it into, so I'll need to be able to adjust things like the overall length, width, and height of the organizer. I'll also want to adjust the wall thickness, the inner corner radius, and the number of compartments along the length of the organizer.

The basic design should be a rectangular box with rounded inner corners created using a hull, and then inner compartments that run the length of the box using a hull as well. I’d like to define the compartments using a variable for the number of columns. Can you generate the parametric OpenSCAD code for this? I'm not using any external libraries.

[Assistant]
organizer_v1.0.scad:
```
//Küchenschublade

$fn=100;
fn=100;

// wall thickness
wt=3;

//length
outerYDM=252;

//width
outerXDM=137.5;

//height
outerZDM=50;

//inner corner radius
innerCR=10;

//compartments
columns=3;

translate([outerXDM/2,0,0])
difference(){

	hull(){
		translate([-outerXDM/2+wt/2, -outerYDM/2+wt/2,outerZDM-wt/2])
		sphere(d=wt);

		translate([-outerXDM/2+wt/2, outerYDM/2-wt/2,outerZDM-wt/2])
		sphere(d=wt);

		translate([outerXDM/2-wt/2, -outerYDM/2+wt/2,outerZDM-wt/2])
		sphere(d=wt);

		translate([outerXDM/2-wt/2, outerYDM/2-wt/2,outerZDM-wt/2])
		sphere(d=wt);

		translate([-outerXDM/2+wt/2, -outerYDM/2+wt/2,1])
		cylinder($fn=fn, h=1, d=wt);

		translate([-outerXDM/2+wt/2, outerYDM/2-wt/2,1])
		cylinder($fn=fn, h=1, d=wt);

		translate([outerXDM/2-wt/2, -outerYDM/2+wt/2,1])
		cylinder($fn=fn, h=1, d=wt);

		translate([outerXDM/2-wt/2, outerYDM/2-wt/2,1])
		cylinder($fn=fn, h=1, d=wt);

		translate([-outerXDM/2+wt/2+1, -outerYDM/2+wt/2+1,0])
		cylinder($fn=fn, h=1, d=wt);

		translate([-outerXDM/2+wt/2+1, outerYDM/2-wt/2-1,0])
		cylinder($fn=fn, h=1, d=wt);

		translate([outerXDM/2-wt/2-1, -outerYDM/2+wt/2+1,0])
		cylinder($fn=fn, h=1, d=wt);

		translate([outerXDM/2-wt/2-1, outerYDM/2-wt/2-1,0])
		cylinder($fn=fn, h=1, d=wt);
	}

	union(){

		inlay();

		for (i=[1:columns-1]){
			translate([i*((outerXDM-(columns+1)*wt)/columns+wt),0,0])
			inlay();
		}
	}
}

module inlay(){

	hull(){
		translate([-outerXDM/2+innerCR/2+wt, -outerYDM/2+innerCR/2+wt,outerZDM])
		sphere(d=innerCR);

		translate([-outerXDM/2+innerCR/2+wt, outerYDM/2-innerCR/2-wt,outerZDM])
		sphere(d=innerCR);

		translate([-outerXDM/2+innerCR/2+wt+(outerXDM-(columns+1)*wt)/columns-innerCR, -outerYDM/2+innerCR/2+wt,outerZDM])
		sphere(d=innerCR);

		translate([-outerXDM/2+innerCR/2+wt+(outerXDM-(columns+1)*wt)/columns-innerCR, outerYDM/2-innerCR/2-wt,outerZDM])
		sphere(d=innerCR);

		translate([-outerXDM/2+innerCR/2+wt, -outerYDM/2+innerCR/2+wt,innerCR/2+wt])
		sphere(d=innerCR);

		translate([-outerXDM/2+innerCR/2+wt, outerYDM/2-innerCR/2-wt,innerCR/2+wt])
		sphere(d=innerCR);

		translate([-outerXDM/2+innerCR/2+wt+(outerXDM-(columns+1)*wt)/columns-innerCR, -outerYDM/2+innerCR/2+wt,innerCR/2+wt])
		sphere(d=innerCR);

		translate([-outerXDM/2+innerCR/2+wt+(outerXDM-(columns+1)*wt)/columns-innerCR, outerYDM/2-innerCR/2-wt,innerCR/2+wt])
		sphere(d=innerCR);
	}
}
```


[User]
I'd like to create a customizable cable tag for small zip ties, designed for easy 3D printing. I need it to be parametric so I can adjust the size, text, and other features. Here are the specific requirements:

**Core Shape and Dimensions:**

*   The main body of the tag should be a rounded rectangle, defined by a length, depth, and height.
*   The rounded corners should be controlled by a radius parameter.
*   I need a parameter for the overall tag thickness and text height.

**Text:**

*   The tag should support both single-line and two-line text. I need parameters for the main text, optional second line of text, and the text size.
*   The text should be centered on the tag's face and extruded outwards. I'm thinking a simple Arial font would be fine.

**Border:**

*   An optional border around the text would be great, with a configurable thickness.

**Zip Tie Holes:**

*   The tag should include four holes for zip ties - one in each corner. I need to be able to adjust the position, size, and radius of these holes.
*   The holes should be designed for typical small zip ties.

**Printing:**

*   I’d like this to be easily printable with layer changes for multi-color printing. The text should be separate from the base.

I expect that the program should be a parametric OpenSCAD file, and it should be fairly straightforward to customize the various parameters. I am not expecting use of external libraries. Could you help me with that?

[Assistant]
label.scad:
```
tag_length = 18;
tag_depth = 12;
tag_border = true;
text_to_display = "R";
text_two_lines = false;
text_to_display_second_line = "Lorem";
text_size = 8;

/* [Hidden] */
tag_height = 1.6;
tag_text_height = 0.6;
tag_radius = 2.5;
tag_zip_tie_hole_dist = 2;
tag_border_thickness = 1;

hole_x = 3.5;
hole_y = 2;
hole_r = 0.5;

module tag_text_single_line() {
    translate([0, 0, tag_height]) {
        linear_extrude(height=2*tag_text_height, center=true, convexity=20) {
            text(text_to_display, size=text_size, font="Arial", halign="center", valign="center");
        }
    }
}

module tag_text_two_lines() {
    translate([0, text_size/2+text_size*0.1, tag_height]) {
        linear_extrude(height=2*tag_text_height, center=true, convexity=20) {
            text(text_to_display, size=text_size, font="Arial", halign="center", valign="center");
        }
    }
    translate([0, -text_size/2-text_size*0.1, tag_height]) {
        linear_extrude(height=2*tag_text_height, center=true, convexity=20) {
            text(text_to_display_second_line, size=text_size, font="Arial", halign="center", valign="center");
        }
    }
}

module tag_text() {
    if (text_two_lines) {
        tag_text_two_lines();
    } else {
        tag_text_single_line();
    }
}

module tag_shape(x,y,z,r) {
    translate([0, 0, z/4]) {
        minkowski() {
            cube([x-2*r, y-2*r, z/2], true);
            cylinder(r=r, h=z/2, $fn=100);
        }
    }
}

module border(x,y,z,r, border_thickness) {
    translate([0, 0, 0]) {
        difference() {
            tag_shape(x, y, z, r);
            tag_shape(x-2*border_thickness, y-2*border_thickness, z, r-border_thickness);
        }
    }
}

module zip_tie_hole() {
    translate([0, 0, tag_height]) {
        minkowski() {
            cube([hole_x-2*hole_r, hole_y-2*hole_r, tag_height*2], true);
            cylinder(r=hole_r, h=tag_height/2, $fn=100);
        }
    }
}

module all_zip_tie_holes() {
    translate([tag_length/2-1.5*tag_border_thickness-hole_x/2, tag_depth/2-1.5*tag_border_thickness-hole_y/2, 0]) {
        zip_tie_hole();
    }
    translate([-tag_length/2+1.5*tag_border_thickness+hole_x/2, tag_depth/2-1.5*tag_border_thickness-hole_y/2, 0]) {
        zip_tie_hole();
    }
    translate([tag_length/2-1.5*tag_border_thickness-hole_x/2, -tag_depth/2+1.5*tag_border_thickness+hole_y/2, 0]) {
        zip_tie_hole();
    }
    translate([-tag_length/2+1.5*tag_border_thickness+hole_x/2, -tag_depth/2+1.5*tag_border_thickness+hole_y/2, 0]) {
        zip_tie_hole();
    }
}

module tag() {
    difference() {
        union() {
            tag_shape(tag_length, tag_depth, tag_height, tag_radius);
            tag_text();
            if (tag_border) {
                translate([0, 0, tag_height]) {
                    border(tag_length, tag_depth, tag_text_height, tag_radius, tag_border_thickness);
                }
            }
        }
        all_zip_tie_holes();
    }
}

tag();

```


[User]
I'd like to design a customizable cable management box, something similar to what I've already got, but I need it from scratch. It should be able to handle different sizes, have slots for cables, and optionally be split into two pieces with connectors for larger prints.

Here are the key features I need:

*   **Parametric Dimensions:** The box should have parameters for internal width, height, and length, as well as wall thickness.
*   **Rounded Corners:** All corners of the box should be rounded with a customizable radius.
*   **Lid:** The box needs a lid that overlaps the box with a customizable overlap height and a tolerance value so it fits properly.
*   **Cable Slots:** I need cable slots on the front and back (or left and right, depending on how you look at it). The slots need to have adjustable width, a start distance from the floor of the box, and offset from the side of the box and whether or not to mirror the slots or align them. The slots should be rounded on the interior.
*   **Splitting:** The box should have an option to split it into two halves along its length. If split, the halves should have integrated connectors to join them. These connectors need to also be parametric so they can be tuned for printer tolerances. I need to be able to specify a gap between the halves when split, as well as the connecting wall thickness and tolerance.
*   **All Parameters Customizable:** I want to be able to tweak all of those things above, so the SCAD file should have variables at the top for all parameters including:

    *   `width` (internal width of the box)
    *   `height` (internal height of the box)
    *   `length` (internal length of the box)
    *   `lid_h` (height of the lid overlap)
    *   `lid_tol` (tolerance for the lid fit)
    *   `corner_r` (radius of the rounded corners)
    *   `slot_w` (width of the cable slots)
    *   `slot_start` (distance from the floor of the box to the beginning of the slot)
    *   `slot_offset` (distance from the side of the box to the cable slot; 0 for centred)
    *    `mirror_offset` (boolean; true to mirror slot offsets, false to align them)
    *   `split` (boolean for whether to split the box)
    *    `gap` (space between the split halves)
    *    `conn_thk` (thickness of the connectors)
    *    `conn_tol` (tolerance for the connectors)
    *   `thk` (wall/floor thickness)
    *   `round` (number of steps in a circle)

I don't need a specific test function, but it should at least include an "All" model, a "Box" model, and a "Lid" model that renders the correct parts. Also if I just render the "All" model, the lid should render beside the box, not on top of it, and if the box is split, the two halves should render side by side with the correct gap between them.

I think that covers everything. I don't think I need any special libraries.

[Assistant]
CableManagementBox.scad:
```
// Which model to render
model = "All";//[All, Box, Lid, Demo, Test]
// Internal width; box will be this wide, plus walls
width = 105;
// Internal height; box will be this high, plus bottom floor
height = 120;
// Internal length; box will be this long, plus walls
length = 295;
// Height of lid overlapping with box
lid_h = 13;
// Lid tolerance
lid_tol = .2;
// Radius of box corners
corner_r = 5;
// Slot width
slot_w = 12;
// Start of slot above floor of box
slot_start = 10;
// Offset of slot from side - set to zero for centred
slot_offset = 75;
// Should slot offsets be mirrored or aligned?
mirror_offset = true;
// If the box should be split in length to fit printer
split = true;
// When splitting the model, space each part this far apart
gap = 15;
// Thickness of walls used to connect a split box
conn_thk = 2;
// Tolerance of above
conn_tol = 0.3;
// Wall/floor thickness
thk = 2;
// Number of steps in a circle
round = 60;

$fn = round;

if (model == "All" || model == "Box" || model == "Demo") {
    demo = model == "Demo";
    hh = height + 15;
    translate([0, 0, demo ? hh - $t * hh /*thk*4*/ : 0])
        box(0);
    if (split) {
        translate([0, demo ? 0 : gap, 0])
            box(1);
    }

    if (model == "All") {
        // Lid
        translate([width + 2 * thk, 0, height + 20])
            rotate([0,180,0]) {
                lid(0);
                if (split) {
                    translate([0, gap, 0])
                        lid(1);
                }
            }
    }
} else if (model == "Lid") {
    lid(0);
    if (split) {
        translate([0, 20, 0])
            lid(1);
    }
} else if (model == "Test") {
    intersection() {
        union() {
            box(0);
            if (split) {
                translate([0, 15, 0])
                    box(1);
            }
        }
        translate([-1, length/2-conn_thk*8, -1])
            cube([width+2*thk+2, conn_thk*16 + 15, height/4 + thk]);
    }
}

module lid(segment) {
    difference() {
        union() {
            // Basic lid top
            roundedRect(width + 2 * thk, thk, length + 2 * thk, corner_r);

            // rim
            translate([thk, thk, thk]) {
                difference() {
                    // rim as a solid
                    roundedRect(width, lid_h, length, corner_r);
                    // remove internals
                    translate([thk + lid_tol, thk + lid_tol, 0])
                        roundedRect(width - 2*(thk + lid_tol), lid_h+1, length -  2*(thk + lid_tol), corner_r);
                    // If box is split, leave room in the lid for the connectors running down the middle of the box sides
                    if (split) {
                        translate([-1, length/2  - 2 * (conn_thk + conn_tol), 0])
                            cube([conn_thk * 4 + conn_tol*2, conn_thk * 4 + conn_tol*4, lid_h+1]);
                        translate([width - 2*thk, length/2  - 2 * (conn_thk + conn_tol), 0])
                            cube([conn_thk * 4 + conn_tol*2, conn_thk * 4 + conn_tol*4, lid_h+1]);
                    }
                }
            }
        }
        // Cut in half for split models
        if (split) {
            if (segment == 0) {
                translate([-1, thk + length/2 - conn_thk - conn_tol/2, -1])
                    cube([width + 2 * thk + 2,  length/2 + 2 * thk + 2, thk + lid_h + 2]);
            } else {
                translate([-1, -1, -1])
                    cube([width + 2*thk + 2,  length/2 + thk + 1 - conn_thk, thk + lid_h + 2]);
            }
        }
    }

    if (split) {
        if (segment == 0) {
            translate([2*thk + 3*conn_thk + conn_tol, thk + length/2 - 2*conn_thk, thk])
                rotate([90, 0, 90])
                    link(conn_thk, conn_tol, width - 2 * thk - 6 * conn_thk, true);
        } else {
            translate([2*thk + 3*conn_thk + conn_tol, thk + length/2 - 2*conn_thk, thk])
                rotate([90, 0, 90])
                    link(conn_thk, conn_tol, width - 2 * thk - 6 * conn_thk, false);
        }
    }
}

module box(segment) {
    difference() {
        // Basic box exterior
        roundedRect(width + 2 * thk, height + thk, length + 2 * thk, corner_r);
        // Remove insides
        translate([thk, thk, thk])
            roundedRect(width, height+0.1, length, corner_r);
        // Slots for cables
        if (slot_offset == 0) { // centre slots
            translate([width/2 + thk - slot_w / 2, -1, thk + slot_start])
                slot(height - slot_start, slot_w, thk + 2);
            translate([width/2 + thk - slot_w / 2, length + thk - 1, thk + slot_start])
                slot(height - slot_start, slot_w, thk + 2);
        } else { // slots offset from sides
            translate([thk + slot_offset, -1, thk + slot_start])
                slot(height - slot_start, slot_w, thk + 2);
            translate([(mirror_offset ? width + thk - slot_offset - slot_w : thk + slot_offset), thk + length - 1, thk + slot_start])
                slot(height - slot_start, slot_w, thk + 2);
        }
        // If splitting box, we need to build out connectors.  This bit is to cut holes in the side of the box.  We could allow the box to grow indefinitely with a bit more work here
        if (split) {
            if (segment == 0) {
                // remove other half of box
                translate([-1, thk + length/2, -1])
                    cube([width + 2 * thk + 2, length, height + thk + 2]);

                // ensure floor of box allows split halves to slide together, side 1
               translate([thk + conn_thk - conn_tol/2, thk + length/2 - conn_thk - conn_tol/2, -1])
                    cube([conn_thk + conn_tol, conn_thk * 2, height + thk + 2]);
               translate([0, thk + length/2 - conn_tol/2, -1])
                    cube([conn_thk * 2, conn_thk + conn_tol, height + thk + 2]);
                // ensure floor of box allows split halves to slide together, side 2
                translate([thk + width - conn_thk * 2 - conn_tol/2, thk + length/2 - conn_thk - conn_tol/2, -1])
                    cube([conn_thk * 3 + 1, conn_thk + conn_tol, height + thk + 2]);
                translate([-1, thk + length/2, -1])
                    cube([width + 2 * thk + 2, 2*conn_thk, thk+1]);
            } else {
                // remove other half of box
                translate([-1, -1, -1])
                    cube([width + 2 * thk + 2, length/2 + thk + 1, height + thk + 2]);
            }
        }
    }

    // If splitting box, we need to build out connectors.  This bit is to cut holes in the side of the box.  We could allow the box to grow indefinitely with a bit more work here
    if (split) {
        connectors(segment == 0);
        if (segment != 0)
            translate([thk + width, thk + length/2 - 1*conn_thk, 0])
                cube([thk, conn_thk, height + thk]);
    }
}

// For a split box, this builds the connectors
module connectors(outer) {
    translate([width + thk, thk + length/2  - (outer ? 2 : 2) *conn_thk, thk])
        rotate([0, 0, 90])
            link(conn_thk, conn_tol, height, outer);

    translate([thk, thk + length/2 + (outer ? 2 : 2) * conn_thk, thk])
        rotate([0, 0, -90])
            link(conn_thk, conn_tol, height, outer);
}

//translate([-10, -10, 0]) slot(height - slot_start, slot_w, thk + 2);

// Cable slot
module slot(h, w, th) {
    translate([0, 0, w/2])
        cube([w, th, h - w/2+0.1]);
    translate([w/2, th, w/2])
        rotate([90,0,0])
            cylinder(h = th, d = w);
    translate([-corner_r, 1.25*th, h - corner_r])
        rotate([90, 0, 0])
            corner(corner_r, corner_r * 2, th+2);
    translate([w + corner_r, -th/3, h - corner_r ])
        rotate([90, 0, 180])
            corner(corner_r, corner_r * 2, th+2);
}

// Helper for rounding corners//45
module corner(rad, wid, ht) {
    difference() {
        cube([wid, wid, ht]);
        translate([0, 0, -1])
            cylinder(h = ht + 2, r = rad);
    }
}

// Build a box with rounded vertical corners
module roundedRect(w, h, l, r) {
    difference() {
        cube([w, l, h]);
        translate([r, r, -1])
            rotate([0,0, 180])
                corner(r, 2 * r, h + 2);
        translate([w - r, r, -1])
            rotate([0,0, 270])
                corner(r, 2 * r, h + 2);
        translate([corner_r, l - r, -1])
            rotate([0,0, 90])
                corner(r, 2 * r, h + 2);
        translate([w - r, l  - r, -1])
            rotate([0,0, 0])
                corner(r, 2 * r, h + 2);
    }
}

module link(t, tl, h, outer) {
    if (!outer) {
            translate([t, 0, 0])
                cube([t, 2 * t, h]);
            translate([t, t, 0])
                cube([2 * t, t, h]);
    } else {
        difference() {
            cube([t * 4, t * 3, h]);
            translate([t - tl/2, -tl/2, -1])
                cube([t + tl, 2 * t + tl, h + 2]);
            translate([t - tl/2, t - tl/2, -1])
                cube([2 * t + tl, t + tl, h + 2]);
        }
    }
}

```


[User]
Hey! I'm trying to design a pill bottle divider, kind of like a rotating carousel inside the bottle, but I need it to be customizable for different bottle shapes and with different numbers of compartments. I found an old design on Thingiverse, but it's not parametric enough for what I need.

I'd like the design to be based on a few key parameters:

*   **Number of compartments:**  I need to be able to specify how many sections the divider has (e.g., 2, 3, 4, up to maybe 8).
*   **Lid thickness:** The rotating lid needs to be a specific thickness, I'd like to set it in millimeters.
*   **Bottle profile:** This is the tricky part. I have a few different bottles. I'll start with parameters like overall bottle depth, top inner diameter, bottom inner diameter. Additionally, some of my bottles have a "punt" or a concave section at the bottom. This is like a series of radial curves. I need to define the shape with a series of 2D coordinates that are [radius, height] pairs and that describe a polygon profile when you rotate it. The coordinate pairs will always start from the center at a specific height of [0,some_height].

I'd like to use a preset list for common bottles if they exist, or at least have a "custom" option where I can specify these parameters for bottles I haven't pre-defined. The preset should ideally include a common bottle from CVS which I know has the code "Berry VLF-20".

The divider itself should have a few features:

*   It should fit snugly inside the bottle, so I'll need a cylinder that matches the inner profile of the bottle.
*   The dividing walls should be vertical and go all the way up to the lid, and be arranged radially from the center of the cylinder.
*   The lid should rotate easily on top of the divider and should be printable. It should have a lip or some type of rotational stop to make sure it doesn't freely spin. A simple tab and indent would be great.
*   The lid should be separate from the main divider, as it will need to rotate separately.

I imagine I'd be using basic OpenSCAD operations like `cylinder()`, `cube()`, `rotate()`, `translate()`, `difference()`, `union()`, `intersection()`, `mirror()`, and `rotate_extrude()`, along with `polygon()` to make it work. I'm not sure if we need any libraries beyond the built-in OpenSCAD ones.

Could you generate a parametric OpenSCAD file (or files) that do this? I don't need any specific dimensions initially, just the parametric version.

[Assistant]
Pill_divider.scad:
```

/* [Separator Parameters] */

// How many  sections do you want?
num_compartments=4; // [2:1:8]
// How thick, in mm should the rotating lid be
lid_thickness = 3; // [1:0.1:5]

/* [Known Bottle Profiles] */

// These will take precedence unless "custom" is selected.
bottle_type = 0; // [0:Custom, 1:Berry VLF-20]

/* [Custom Bottle parameters] */

// How deep is the bottle from rim to lowest point?
bottle_depth=68.8; // [0:0.1:150]
// Inner diameter of bottle
bottle_top_d=37.6; // [0:0.1:50]
// Inner diameter at bottom of bottle
bottle_bottom_d=37.0; //[0:0.1:50]
// Profile of the "punt" or "lift ups" (aka the little indents) in the bottom of the bottle. Give 2D coordinates as [[radius, height], [radius, height],...] starting from the center and working out
punt_profile=[[0,2],[5.5,2],[7.2,0.4],[18.8,0]];

/* [Hidden] */
bottle_profiles =
[[], // Custom
[68.8, 37.6, 37.0, [[0,2],[5.5,2],[7.2,0.4],[18.8,0]]], // Berry VLF-20
];
if (bottle_type) {
  assert(bottle_type >= 0 && bottle_type < len(bottle_profiles), "Invalid bottle type!");
  profile = bottle_profiles[bottle_type];
  bottle_depth = profile[0];
  bottle_top_d = profile[1];
  bottle_bottom_d = profile[2];
  punt_profile = profile[3];
}
insert_r = bottle_top_d/2;
insert_h = bottle_depth - lid_thickness;

$fn = 100;

//opening_r = insert_r - 4;
//opening_r = 3/6 * insert_r - 4;

difference(){
intersection(){
union(){
cylinder(h = bottle_depth-3, r=4);
for (i = [1:num_compartments]){
  rotate([0,0,(i + 0.5) * 360/num_compartments])
  translate([0,-1,0])
  cube([insert_r,2,insert_h]);
}
}
cylinder(h=insert_h, d1=bottle_top_d, d2=bottle_bottom_d);
}
cylinder(h = 5.25, r=2.6);

translate([0,0, insert_h])
mirror([0,0,1])
rotate_extrude()
polygon(concat(punt_profile, [[0,-0.1]]));
}

translate([bottle_top_d*1.5,0,0])
union(){
  difference(){
    cylinder(h=lid_thickness, r = insert_r);
    translate([insert_r,0,-0.5])
   scale([insert_r-4,0.75*insert_r*sin(180/num_compartments),1])
   cylinder(h=4,r=1);
  }
  cylinder(h = 8, r=2.5);
}

```


[User]
I'd like to create a parametric OpenSCAD design for generating Braille text on a raised plate. I have a basic idea of the layout but I need help to make it configurable.

Here's what I'm looking for:

1.  **Text Input:** The design should accept a text string as input and convert it to Braille. It should be able to handle both lowercase and uppercase letters, as well as numbers. It needs to use the correct Braille prefixes for capitalization and numbers.
2.  **Braille Conversion:** The Braille conversion should correctly map characters to their respective Braille dot patterns. I'm assuming standard 6-dot Braille.
3.  **Dot Representation:** Each Braille dot should be represented by a raised hemisphere or a cylinder - either would work, as long as it's customizable. The dot should have a configurable diameter, and there needs to be configurable spacing between the dots both horizontally and vertically, and also configurable spacing between cells.
4.  **Plate Generation:** I'd like to generate a plate that surrounds the braille text, and the plate needs to be configurable with a padding value (so the braille is not right up against the edge), and also with a rounded corner radius. The plate should be a solid extrusion.
5.  **Output:** I need to be able to output the design as a 3D model, likely an STL.
6.  **Customization:** All dimensions, such as dot diameter, dot spacing, cell spacing, plate padding, corner radius, and the thickness of the plate should be customizable. I also need the option to just generate the braille text without a backing plate, as this will be for use in different circumstances.
7. **Library Usage:** I don't want to rely on any external libraries beyond what comes with OpenSCAD, but if you could make use of a library, that would be fine, as long as it's commonly used and easily available.

Could you generate the necessary SCAD file(s) for this? It will be a big help.

[Assistant]
braille.scad:
```
$fn = 30;

input_text = "Hello world";
generate_plate = "yes";
padding = 2;
r = 1;

function char_is_number(c) = ord_(c) >= ord_("0") && ord_(c) <= ord_("9");

function char_is_uppercase(c) = ord_(c) >= ord_("A") && ord_(c) <= ord_("Z");

function is_numeric(s, i=0) = i == len(s) ? true : char_is_number(s[i]) && is_numeric(s, i+1);

function is_uppercase(s, i=0) = i == len(s) ? true : char_is_uppercase(s[i]) && is_uppercase(s, i+1);

function ord_(c) = version().x >= 2019 ? ord_(c) : workaroud_ord(c);

function get_braille_prefix(word) =
    len(word) == 0        ? []                        :
    is_numeric(word)      ? BRAILLE_NUMBER_PREFIX     :
    is_uppercase(word[0]) ? BRAILLE_CAPITALIZE_PREFIX :
    is_uppercase(word)    ? BRAILLE_UPPERCASE_PREFIX  :
    [];

function get_braille_char(c) = BRAILLE_CHARS[ord_(c)];

function word_to_braille(word) = [for (c = str_to_vector(word)) get_braille_char(c)];

function append_to_braille(braille, word) = concat(
    braille,
    [BRAILLE_SPACE],
    get_braille_prefix(word),
    word_to_braille(word)
    );

function words_to_braille(words, i=0, braille=[]) =
    i == len(words) ? braille :
    len(braille) == 0 ? words_to_braille(words, i+1, word_to_braille(words[i])) :
    words_to_braille(words, i+1, append_to_braille(braille, words[i]));

function split_words(s, i=0, words=[]) =
    i == len(s) ? words :
    s[i] == " " ? split_words(s, i+1, concat([for (w = words) w], [""])) :
    len(words) == 0 ? split_words(s, i+1, [s[i]]) :
    len(words) == 1 ? split_words(s, i+1, [str(words[0], s[i])]) :
    split_words(
        s,
        i+1,
        concat([for (j=[0:(len(words) - 2)]) words[j]],
               [str(words[len(words)-1], s[i])]
            )
        );

function str_to_braille(s) = words_to_braille(split_words(s));

function braille_dimensions(s) = [
    (len(str_to_braille(s)) - 1) * CELL_DISTANCE + DOT_DIAMETER + DOT_DISTANCE,
    2 * DOT_DISTANCE + DOT_DIAMETER
    ];

module braille_dot(dot, 2d=false, $fn=$fn) {
    if (dot) {
        if (2d) {
            circle(d=DOT_DIAMETER, $fn=$fn);
        }
        else {
            intersection() {
                sphere(d=DOT_DIAMETER, $fn=$fn);
                translate([-5, -5, 0]) cube(10);
            }
        }
    }
}

module braille(s, $fn=$fn, 2d=false) {
    braille = str_to_braille(s);

    echo(braille);

    if (len(braille) > 0) {
        for (
            i = [0:(len(braille) - 1)],
                j = [0:2],
                k = [0, 1]
            ) {
            matrix = braille[i];

            #translate(
                [
                    i * CELL_DISTANCE + k * DOT_DISTANCE + DOT_DIAMETER / 2,
                    (2 - j) * DOT_DISTANCE + DOT_DIAMETER / 2,
                    0
                    ]) {
                braille_dot(matrix[j][k], $fn=$fn, 2d=2d);
            }
        }
    }
}

function str_to_vector(s) = [for (i=[0:(len(s)-1)]) s[i]];

function workaroud_ord(c) =
    c == "0" ? 48 :
    c == "1" ? 49 :
    c == "2" ? 50 :
    c == "3" ? 51 :
    c == "4" ? 52 :
    c == "5" ? 53 :
    c == "6" ? 54 :
    c == "7" ? 55 :
    c == "8" ? 56 :
    c == "9" ? 57 :
    c == "a" ? 97 :
    c == "b" ? 98 :
    c == "c" ? 99 :
    c == "d" ? 100 :
    c == "e" ? 101 :
    c == "f" ? 102 :
    c == "g" ? 103 :
    c == "h" ? 104 :
    c == "i" ? 105 :
    c == "j" ? 106 :
    c == "k" ? 107 :
    c == "l" ? 108 :
    c == "m" ? 109 :
    c == "n" ? 110 :
    c == "o" ? 111 :
    c == "p" ? 112 :
    c == "q" ? 113 :
    c == "r" ? 114 :
    c == "s" ? 115 :
    c == "t" ? 116 :
    c == "u" ? 117 :
    c == "v" ? 118 :
    c == "w" ? 119 :
    c == "x" ? 120 :
    c == "y" ? 121 :
    c == "z" ? 122 :
    c == "A" ? 65 :
    c == "B" ? 66 :
    c == "C" ? 67 :
    c == "D" ? 68 :
    c == "E" ? 69 :
    c == "F" ? 70 :
    c == "G" ? 71 :
    c == "H" ? 72 :
    c == "I" ? 73 :
    c == "J" ? 74 :
    c == "K" ? 75 :
    c == "L" ? 76 :
    c == "M" ? 77 :
    c == "N" ? 78 :
    c == "O" ? 79 :
    c == "P" ? 80 :
    c == "Q" ? 81 :
    c == "R" ? 82 :
    c == "S" ? 83 :
    c == "T" ? 84 :
    c == "U" ? 85 :
    c == "V" ? 86 :
    c == "W" ? 87 :
    c == "X" ? 88 :
    c == "Y" ? 89 :
    c == "Z" ? 90 :
    undef;

DOT_DIAMETER = 1.5;
DOT_DISTANCE = 2.4;
CELL_DISTANCE = 6;

BRAILLE_NUMBER_PREFIX = [
    [[false, true], [false, true], [true, true]]
    ];

BRAILLE_CAPITALIZE_PREFIX = [
    [[false, true], [false, false], [false, true]]
    ];

BRAILLE_UPPERCASE_PREFIX = [
    [[false, true], [false, false], [false, true]],
    [[false, true], [false, false], [false, true]]
    ];

BRAILLE_SPACE = [[false, false], [false, false], [false, false]];

BRAILLE_CHARS = [
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    [[false, true], [true, true], [false, false]],
    [[true, false], [false, false], [false, false]],
    [[true, false], [true, false], [false, false]],
    [[true, true], [false, false], [false, false]],
    [[true, true], [false, true], [false, false]],
    [[true, false], [false, true], [false, false]],
    [[true, true], [true, false], [false, false]],
    [[true, true], [true, true], [false, false]],
    [[true, false], [true, true], [false, false]],
    [[false, true], [true, false], [false, false]],
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    [[true, false], [false, false], [false, false]],
    [[true, false], [true, false], [false, false]],
    [[true, true], [false, false], [false, false]],
    [[true, true], [false, true], [false, false]],
    [[true, false], [false, true], [false, false]],
    [[true, true], [true, false], [false, false]],
    [[true, true], [true, true], [false, false]],
    [[true, false], [true, true], [false, false]],
    [[false, true], [true, false], [false, false]],
    [[false, true], [true, true], [false, false]],
    [[true, false], [false, false], [true, false]],
    [[true, false], [true, false], [true, false]],
    [[true, true], [false, false], [true, false]],
    [[true, true], [false, true], [true, false]],
    [[true, false], [false, true], [true, false]],
    [[true, true], [true, false], [true, false]],
    [[true, true], [true, true], [true, false]],
    [[true, false], [true, true], [true, false]],
    [[false, true], [true, false], [true, false]],
    [[false, true], [true, true], [true, false]],
    [[true, false], [false, false], [true, true]],
    [[true, false], [true, false], [true, true]],
    [[false, true], [true, true], [false, true]],
    [[true, true], [false, false], [true, true]],
    [[true, true], [false, true], [true, true]],
    [[true, false], [false, true], [true, true]],
    undef,
    undef,
    undef,
    undef,
    undef,
    undef,
    [[true, false], [false, false], [false, false]],
    [[true, false], [true, false], [false, false]],
    [[true, true], [false, false], [false, false]],
    [[true, true], [false, true], [false, false]],
    [[true, false], [false, true], [false, false]],
    [[true, true], [true, false], [false, false]],
    [[true, true], [true, true], [false, false]],
    [[true, false], [true, true], [false, false]],
    [[false, true], [true, false], [false, false]],
    [[false, true], [true, true], [false, false]],
    [[true, false], [false, false], [true, false]],
    [[true, false], [true, false], [true, false]],
    [[true, true], [false, false], [true, false]],
    [[true, true], [false, true], [true, false]],
    [[true, false], [false, true], [true, false]],
    [[true, true], [true, false], [true, false]],
    [[true, true], [true, true], [true, false]],
    [[true, false], [true, true], [true, false]],
    [[false, true], [true, false], [true, false]],
    [[false, true], [true, true], [true, false]],
    [[true, false], [false, false], [true, true]],
    [[true, false], [true, false], [true, true]],
    [[false, true], [true, true], [false, true]],
    [[true, true], [false, false], [true, true]],
    [[true, true], [false, true], [true, true]],
    [[true, false], [false, true], [true, true]],
    undef,
    undef,
    undef,
    undef,
    undef
    ];

if (generate_plate == "yes") {
    union() {
        dimensions = braille_dimensions(input_text) + 2 * padding * [1, 1];

        translate([r, r, 0])
            hull() {
            cylinder(r=1);
            translate([dimensions.x - 2*r, 0, 0]) cylinder(r=1);
            translate([0, dimensions.y - 2*r, 0]) cylinder(r=1);
            translate([dimensions.x - 2*r, dimensions.y - 2*r, 0]) cylinder(r=1);
        }

        translate([padding, padding, 1]) {
            braille(input_text);
        }
    }
}
else {
    braille(input_text);
}

/* braille("oi"); */
/* echo(word_to_braille("oi")); */
/* echo(get_braille_char("o")); */
/* echo([for (c = str_to_vector("oi")) get_braille_char(c)]); */

```