[User]
Hey, I'm looking to design a pretty complex dice tray and I'd like some help generating the OpenSCAD code. I've seen some similar designs, but I want something that's highly customizable and includes a few specific features.

The core of the design is a rectangular box that will act as the base of the tray. Inside, I need to have a few different sections: a storage area at the back, a dice shelf at the front, and a main dice rolling area in between. Crucially, the back storage section needs to be able to hold small, removable containers – either individual bins, or shallow trays that can stack on top of each other. I’m thinking of a system where you can specify how many bins fit side by side in the storage section (1, 2, 3, or 4).  I'd also like a way to render just the storage bins or just the trays without rendering the entire assembly. The size of these bins or trays needs to be parametric as well, so I can adjust the gaps and overall dimensions.

The whole tray also needs to act as a stand for a tablet, which will rest against the back edge when the lid is removed. The angle at which the tablet reclines should also be adjustable using a parameter. To ensure the tablet sits well, there will be a cutout in the back of the base that allows the tablet to lean back at the desired angle. When the lid is removed, it will slot into this cutout to provide the back support.

The lid will need a way to close securely. I was thinking of incorporating a small nub or tab, and a matching socket to keep it shut. I’d also like the option for a little finger access cutout near the top edge of the box, so I can get a good grip on the lid when removing it. The lid should also fit snugly, but I'd also like a parameter to adjust the tolerances here. I'd also like a tolerance test print so that i can test the lid fit before printing the whole thing.

For a final touch, it would be great if the design included an optional space for a light bar, so it can light up the dice rolling area.  The light should have its own parameters to adjust its size, angle, and how much it is inset into the base.

So, to sum up, I'd like a parametric OpenSCAD design for a dice tray that includes the following features:

*   A main box with adjustable overall dimensions.
*   A storage section at the back with parametric bins or trays (1, 2, 3, or 4 containers wide). Ability to render just the bins, just the trays, or the whole thing.
*   A dice shelf at the front.
*   A main dice rolling area.
*   A tablet stand/lid slot in the back with an adjustable recline angle.
*   A lid with a closing nub, finger access, and adjustable tolerances.
*   An optional light bar cutout with adjustable parameters.
*   A test print for lid tolerance.

The following parameters should be adjustable:

*   Overall box dimensions (width, depth, height).
*   Tablet thickness and recline angle.
*   Storage section depth.
*   Dice shelf depth.
*   Light bar depth, height, angle, and inset percentage.
*   Lid gap, lid slot gap, tablet gap, and storage container gap.
*   Wall thickness, base thickness, lid thickness, divider thickness, and storage container thickness.

I'd like to avoid using any libraries for this if possible, but if one absolutely must be used, then let's use BOSL2. Please generate the necessary `.scad` files based on this description.

[Assistant]
Dice_Tray_Deluxe.scad:
```
//////////////////////////////////////////////////
// Parameters
//////////////////////////////////////////////////
/* [What to generate] */
//What element/s to render
What_To_Render = "Box"; //[Box, Lid, Storage Bin x1, Storage Bin x2, Storage Bin x3, Storage Bin x4, Storage Tray, Lid Fit Test]

/* [Tablet] */
//How thick the tablet is with any case
Tablet_Thickness = 17;
//What percent angle to recline the tablet at
Tablet_Angle = 20; //[5:35]

/* [Box dimensions] */
//Width of the whole box (side to side)
Overall_Box_Width = 170;
//Depth of the whole box (front-end to back-end)
Overall_Box_Depth = 200;
//Height of the whole box (base to lid)
Overall_Box_Height = 38;

/* [Manual section dimensions] */
//How deep (front to back) the the storage section at the back is
Storage_Depth = 30;
//How deep (front to back) the dice shelf at the front is (about 2/3 dice diameter)
Shelf_Depth = 22;

/* [Light] */
//To include a cutout for a light bar
Include_Light = "No"; //[Yes, No]
//Depth of the light bar (long-edge to long-edge)
Light_Depth = 40;
//Height of the light bar (led-side to back-side)
Light_Height = 11;
//Angle of the LED light in degrees
Light_Angle = 35;
//Percentage light is inset. Less than 55 may tip over
Light_Inset_Percentage = 60; //[55:100]

/* [Tollerance for better fits] */
//Gap left between lid and the box's top/rail surfaces
Lid_Gap = 0.40;
//Gap left around the lid when it is in used as the tablet backrest
Lid_Slot_Gap = 0.5;
//Gap left around tablet's thickness
Tablet_Gap = 1;
//Storage section containers gap, around each container surface
Storage_Gap = 1;

//End parameters section
module endParams(){};endParams();

//////////////////////////////////////////////////
// Parameters - hard coded/calculated
//////////////////////////////////////////////////

//Epsilon, used to ensure clean cuts (avoid overlapping surfaces when differencing)
eps = 0.007;
//Level of detail of rounded bits
$fn=32;

/* [Surface thicknesses, I removed these from the optional params cause you can mess stuff up by making 1 too small (lid) and 1 too large (wall thickness)] */
//How thick outer walls are
Wall_Thickness = 4; //[4:8]
//How thick the base is
Base_Thickness = 4; //[4:8]
//How thick the lid is
Lid_Thickness = 4; //[3:8]
//How thick the inner dividing walls are
Divider_Thickness = 2;
//How thick the individual storage container surfaces are
Storage_Container_Thickness = 1;

//Base of box, not counting lid structure or its retaining bracker
box_ext_x = Overall_Box_Width;
box_ext_y = Overall_Box_Depth;
box_ext_z = Overall_Box_Height - Lid_Thickness;

//Interior area of box, which is enclosed by the lid
box_int_x = box_ext_x - (Wall_Thickness * 2);
box_int_y = box_ext_y - (Wall_Thickness * 2);
box_int_z = box_ext_z - Base_Thickness;

//Lid
lid_x = box_ext_x - (Lid_Gap*2) - Wall_Thickness;
lid_y = box_ext_y - Lid_Gap - Wall_Thickness;
lid_z = Lid_Thickness - Lid_Gap;

//Lid slot cutout
lidSlot_cutout_x = box_ext_x + eps;
lidSlot_cutout_y = box_ext_y;
lidSlot_cutout_z = Lid_Thickness + (Lid_Slot_Gap * 2);

//Tablet slot cutout
tablet_cutout_x = box_ext_x + eps;
tablet_cutout_y = box_ext_z;
tablet_cutout_z = Tablet_Thickness + (Tablet_Gap * 2);
tablet_depth = 6; //desire MM below top surface for higher of the 2 bottom edge
tablet_depth_angled = tablet_depth / cos(Tablet_Angle);
//Offsets for y positioning of tablet cutout
tablet_yOffsetFromLidSlit = lidSlot_cutout_z / cos(Tablet_Angle);
tablet_yOffsetWhenAngled = tablet_cutout_z / cos(Tablet_Angle);

//List slot cutout depths (based off tablet's depth)
lidSlot_startPoint = lidSlot_cutout_z * sin(Tablet_Angle);
lidSlot_evenToTablet = lidSlot_startPoint + tablet_depth_angled;
lidSlot_depth = lidSlot_evenToTablet + (tablet_cutout_z * sin(Tablet_Angle)) + tablet_depth; //desired MM below top surface for bottom-most edge
lidSlot_depth_angled = lidSlot_depth / cos(Tablet_Angle);

//Divider walls and floor-raisers. z of 0 means a filled area, box_int_z = full height of box interior
//Storage section (back part)
div_storage_x = box_int_x;
div_storage_y = Storage_Depth;
div_storage_z = box_int_z;
//Tablet section (holds tablet + lid when box is open)
div_tablet_x = box_int_x;
div_tablet_y = tablet_yOffsetFromLidSlit + tablet_yOffsetWhenAngled + (tablet_depth*sin(Tablet_Angle)) + Wall_Thickness;
div_tablet_z = 0;
//Stoop (dice rest at front of box) (also called shelf)
div_stoop_x = box_int_x;
div_stoop_y = Shelf_Depth; //max size of dice * mult
div_stoop_z = 5;
//Tray (area where you roll dice)
div_tray_x = box_int_x;
div_tray_y = box_int_y - div_storage_y - div_tablet_y - div_stoop_y;
div_tray_z = box_int_z;

//Tester is for the lid fid test print
//outside of tester's bottom part (not the lid section)
tester_body_ext_x = Overall_Box_Width;//Wall_Thickness*2+100;
tester_body_ext_y = 30;//Wall_Thickness*2+20;
tester_body_ext_z = Lid_Thickness;
//inside of the tester's bottom part
tester_body_int_x = tester_body_ext_x - Wall_Thickness*2;
tester_body_int_y = tester_body_ext_y - Wall_Thickness*2;
tester_body_int_z = tester_body_ext_z;
//Tester lid
tester_lid_x = tester_body_ext_x - (Lid_Gap*2) - Wall_Thickness;
tester_lid_y = tester_body_ext_y - Lid_Gap - Wall_Thickness;
tester_lid_z = Lid_Thickness - Lid_Gap;

//Lid closure nub (helps keep lid closed)
//Socket side
lidNub_socket_x = 20;//Wall_Thickness*3;
lidNub_socket_y = Wall_Thickness/2;
lidNub_socket_z = Wall_Thickness/2;
//Nub side
lidNub_nub_x = lidNub_socket_x - Lid_Gap;
lidNub_nub_y = lidNub_socket_y - Lid_Gap;
lidNub_nub_z = lidNub_socket_z - Lid_Gap;
//Extra bit near the lid nub so you can lift it up to open it easier
lidNub_tab_x = lidNub_socket_x+10;
lidNub_tab_y = 6;
lidNub_tab_z = lid_z;

//Rail cutout so you can get more levage to remove lid
railFingerAccess_x = 25;
railFingerAccess_y = Wall_Thickness;
railFingerAccess_z = Lid_Thickness;

//Storage section containers
//Storage section aria where boxes can exist, with gaps removed
sSect_x = div_storage_x - (Storage_Gap*2);
sSect_y = div_storage_y - (Storage_Gap*2) - (Divider_Thickness/2);
sSect_z = box_int_z - Storage_Gap;

//Storage Tray
sTray_x = sSect_x;
sTray_y = sSect_y;
sTray_z = (sSect_z / 2) - Storage_Gap;

//////////////////////////////////////////////////
// Logic
//////////////////////////////////////////////////

//Render dice tray
if (What_To_Render == "Box"){
    create_box();
}

//Render lid to dice tray
else if (What_To_Render == "Lid"){
    create_lid();
}

//Render the lid fit test module
else if (What_To_Render == "Lid Fit Test"){
    offset = tester_body_ext_y/2;

    translate([0, offset,0])
    create_testerBox();

    translate([0,-offset,0])
    create_testerLid();
}

//Render stoage containers, by how many you want to fit side by side
else if (What_To_Render == "Storage Bin x1"){
    create_storageBox(1);
}
else if (What_To_Render == "Storage Bin x2"){
    create_storageBox(2);
}
else if (What_To_Render == "Storage Bin x3"){
    create_storageBox(3);
}
else if (What_To_Render == "Storage Bin x4"){
    create_storageBox(4);
}

//Render storage trays
else if (What_To_Render == "Storage Tray"){
    create_storageTray();
}

module part_storageTray(){
    zOfTopOfTray = sTray_z;
    zDepthOfInterior = zOfTopOfTray - Storage_Container_Thickness;

    //tray
    difference(){
        create_storageBox(1);
        subPart_cutoutForTopOfStarterBox();
    }
    //finger pulls
    subPart_fingerPull();
    rotate([0,0,180])
    subPart_fingerPull();

    module subPart_cutoutForTopOfStarterBox(){
        xSize = sSect_x + eps;
        ySize = sSect_y + eps;
        zSize = sSect_z;

        xPos = 0;
        yPos = 0;
        zPos = zOfTopOfTray;

        translate([xPos,yPos,zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }

    module subPart_fingerPull(){
        xSize = zDepthOfInterior / 2;
        ySize = sSect_y;
        zSize = zDepthOfInterior;

        p1 = [0,0];
        p2 = [xSize,0];
        p3 = [0,zSize];

        xPos = -(sSect_x/2 - Storage_Container_Thickness);
        yPos = -ySize/2;
        zPos = zOfTopOfTray;

        translate([xPos,yPos,zPos])
        rotate([-90,0,0])
        linear_extrude(ySize)
        polygon([p1, p2, p3]);

    }
}

//////////////////////////////////////////////////
// Create
//////////////////////////////////////////////////

module create_box(){
    difference(){
        //The box
        union(){
            //Outer shell of box
            part_boxShell();
            //Divider sections and floors (breaking up the areas)
            part_storageDivider();
            part_stoopDivider();
            part_tabletDivider();
            //Rails on top of box
            part_lidRails();
        }
        //Cutout for led
        if (Include_Light=="Yes"){
            part_lightbarCutout();
        }
        //Cutout for tablet and lid
        part_lid_cutout();
        part_tablet_cutout();
    }
}

module create_lid(){
    part_lid();
}

module create_testerBox(){
    union(){
        part_tester_body();
        part_lidRails(tester=true);
    }
}

module create_testerLid(){
    part_lid(tester=true);
}

module create_storageBox(howManyBoxesFit){
    part_storageBox(howManyBoxesFit);
}

module create_storageTray(){
    union(){
        part_storageTray();
    }
}

//////////////////////////////////////////////////
// Parts
//////////////////////////////////////////////////

//Create shell of the box, not including the lid section
module part_boxShell(){
    difference(){
        subpart_body();
        subpart_containerCutout();
        subpart_nubCutout();
    }

    //Sub-parts that are used to make this part
    module subpart_body(){
        xSize = box_ext_x;
        ySize = box_ext_y;
        zSize = box_ext_z;

        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_containerCutout(){
        xSize = box_int_x;
        ySize = box_int_y;
        zSize = box_int_z+eps;
        xPos = 0;
        yPos = 0;
        zPos = Base_Thickness;

        translate([xPos,yPos,zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_nubCutout(){
        xSize = lidNub_socket_x;
        ySize = lidNub_socket_y;
        zSize = lidNub_socket_z;

        xPos = 0;
        yPos = -(box_ext_y/2 - Wall_Thickness/2);
        zPos = box_ext_z;

        translate([xPos,yPos,zPos])
        roundedCylinder([xSize, ySize, zSize]);
    }
}

//Divider wall for back storage compartment
module part_storageDivider(){
    xSize = div_storage_x;
    ySize = Divider_Thickness;
    zSize = div_storage_z;

    xPos = 0;
    yPos = box_int_y/2 - div_storage_y;
    zPos = Base_Thickness;

    translate([xPos, yPos, zPos])
    cubeZAligned([xSize, ySize, zSize]);
}

//Divider wall for the front dice shelf thing
module part_stoopDivider(){
    xPos = 0;
    yPos = -(box_int_y/2 - div_stoop_y);
    zPos = Base_Thickness;

    translate([xPos, yPos, zPos]){
        subpart_divider();
        subpart_riser();
    }

    module subpart_divider(){
        xSize = div_stoop_x;
        ySize = Divider_Thickness;
        zSize = box_int_z;

        xPos = 0;
        yPos = 0;
        zPos = 0;

        translate([xPos, yPos, zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_riser(){
        xSize = div_stoop_x;
        ySize = div_stoop_y;
        zSize = box_int_z - div_stoop_z;

        xPos = 0;
        yPos = -(ySize/2);
        zPos = 0;

        translate([xPos, yPos, zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
}

//Area where the tablet cutout will go
module part_tabletDivider(){
    xPos = 0;
    yPos = (box_int_y/2 - div_storage_y - div_tablet_y);
    zPos = Base_Thickness;

    translate([xPos, yPos, zPos]){
        subpart_divider();
        subpart_filler();
    }

    module subpart_divider(){
        xSize = div_tablet_x;
        ySize = Divider_Thickness;
        zSize = box_int_z;

        xPos = 0;
        yPos = 0;
        zPos = 0;

        translate([xPos, yPos, zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_filler(){
        xSize = div_tablet_x;
        ySize = div_tablet_y;
        zSize = box_int_z-div_tablet_z;

        xPos = 0;
        yPos = (ySize/2);
        zPos = 0;

        translate([xPos, yPos, zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
}

//Cutout for where the lightbar sits
module part_lightbarCutout(){
    xSize = box_ext_x + eps;
    ySize = Light_Depth;
    zSize = Light_Height;

    xPos = 0;
    yPos = box_int_y/2 - div_storage_y - div_tablet_y - (Divider_Thickness/2) - eps;
    zOffset = (Light_Inset_Percentage/100) * (ySize*sin(Light_Angle)) - Lid_Thickness;
    zPos = box_ext_z + (zSize*cos(Light_Angle)) - zOffset;

    translate([xPos,yPos,zPos])
    subpart_shape();

    module subpart_shape(){
        width = box_ext_x + eps;

        p1 = [0,0];
        p2 = [zSize * sin(Light_Angle), -zSize * cos(Light_Angle)];
        p3 = [p2.x + (ySize * cos(Light_Angle)), p2.y + (ySize * sin(Light_Angle))];
        p4 = [p3.x, box_ext_z];
        p5 = [0, p4.y];

        translate([width/2,0,0])
        rotate([90,0,-90])
        linear_extrude(width)
        polygon([p1,p2,p3, p4, p5]);
    }
}

//Cutout for where the lid goes
module part_lid_cutout(){
    xPos = 0;
    yPos = box_int_y/2 - div_storage_y;
    zPos = box_ext_z;

    translate([xPos, yPos, zPos])
    subpart_lid();

    module subpart_lid(){
        xSize = lidSlot_cutout_x;
        ySize = lidSlot_cutout_y;
        zSize = lidSlot_cutout_z;

        xPos = 0;
        yPos = ySize/2 - lidSlot_depth_angled;
        zPos = 0;

        xRot = 90-Tablet_Angle;
        yRot = 0;
        zRot = 0;

        rotate([xRot,yRot,zRot])
        translate([xPos, yPos, zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
}

//Cutout for the tablet
module part_tablet_cutout(){
    xPos = 0;
    yPos = box_int_y/2 - div_storage_y - tablet_yOffsetFromLidSlit - tablet_yOffsetWhenAngled-eps;
    zPos = box_ext_z;

    translate([xPos, yPos, zPos])
    subpart_tablet();

    module subpart_tablet(){
        xSize = tablet_cutout_x;
        ySize = tablet_cutout_y;
        zSize = tablet_cutout_z + eps;

        xPos = 0;
        yPos = ySize - tablet_depth_angled;
        zPos = 0;

        xRot = 90-Tablet_Angle;
        yRot = 0;
        zRot = 0;

        rotate([xRot,yRot,zRot])
        translate([xPos, yPos, zPos])
        cubeYTopAligned([xSize, ySize, zSize]);
    }
}

//Create rails that go on top of box to hold lid on
//Tester resizes them to fit the test print
module part_lidRails(tester=false){
    ext_x = tester ? tester_body_ext_x : box_ext_x;
    ext_y = tester ? tester_body_ext_y : box_ext_y;
    ext_z = tester ? tester_body_ext_z : box_ext_z;

    xPos = 0;
    yPos = 0;
    zPos = ext_z;

    translate([xPos, yPos, zPos])
    union(){
        difference(){
            subpart_block();
            subpart_cutout();
            subpart_fingerGap();
        }
        subpart_railGuide("left");
        subpart_railGuide("right");
        subpart_fingerGap_bumpers();
    }

    module subpart_block(){
        xSize = ext_x;
        ySize = ext_y;
        zSize = Lid_Thickness;

        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_cutout(){
        xSize = ext_x - Wall_Thickness;
        ySize = ext_y - Wall_Thickness + eps;
        zSize = Lid_Thickness + eps;

        xPos = 0;
        yPos = -Wall_Thickness/2 - eps;
        zPos = -eps/2;

        translate([xPos, yPos, zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_railGuide(side){
        sideMod = side=="left" ? 1 : -1;
        length = ext_y - Wall_Thickness;

        xPos = sideMod * (-ext_x/2 + Wall_Thickness/2) ;
        yPos = length/2 - Wall_Thickness/2;
        zPos = 0;

        xRot = 90;
        yRot = 0;
        zRot = 0;

        p1 = [0,0];
        p2 = [0,Lid_Thickness];
        p3 = [sideMod * (Wall_Thickness/2),Lid_Thickness];

        translate([xPos, yPos, zPos])
        rotate([xRot, yRot, zRot])
        linear_extrude(length)
        polygon([p1,p2,p3]);
    }
    module subpart_fingerGap(){
        //railFingerAccess_x
        xSize = railFingerAccess_x;
        ySize = railFingerAccess_y + eps*2;
        zSize = railFingerAccess_z + eps;

        xPos = 0;
        yPos = ext_y/2 - ySize/2 + eps/2;
        zPos = -eps/2;

        translate([xPos, yPos, zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_fingerGap_bumpers(){
        //Same xyzSize and Pos as subpart_fingerGap
        xSize = railFingerAccess_x;
        ySize = railFingerAccess_y + eps*2;
        zSize = railFingerAccess_z + eps;

        xPos = 0;
        yPos = ext_y/2 - ySize/2 + eps/2;
        zPos = -eps/2;

        translate([xPos, yPos, zPos]){
            translate([xSize/2,0,0])
            cylinder(d=ySize, h=zSize);
            translate([-xSize/2,0,0])
            cylinder(d=ySize, h=zSize);
        }
    }
}

//Create basic lid shape/structure
module part_lid(tester=false){
    lid_x = tester ? tester_lid_x : lid_x;
    lid_y = tester ? tester_lid_y : lid_y;
    lid_z = tester ? tester_lid_z : lid_z;

    xPos = 0;
    yPos = 0;
    zPos = lid_z;

    xRot = 0;
    yRot = 180;
    zRot = 0;

    translate([xPos, yPos, zPos])
    rotate([xRot, yRot, zRot])
    difference(){
        subpart_block();

        subpart_trimShape("left");
        subpart_trimShape("right");
    }
    subpart_nub();
    subpart_exraNearNub();

    module subpart_block(){
        xSize = lid_x;
        ySize = lid_y;
        zSize = lid_z;

        cubeZAligned([xSize, ySize, zSize]);
    }

    module subpart_trimShape(side){
        sideMod = side=="left" ? 1 : -1;
        length = lid_y + eps;

        xPos = sideMod * (-lid_x/2 - eps);
        yPos = length/2;
        zPos = -eps/2;

        xRot = 90;
        yRot = 0;
        zRot = 0;

        p1 = [0,0];
        p2 = [0,lid_z + eps];
        p3 = [sideMod * (Wall_Thickness/2),lid_z + eps];

        translate([xPos, yPos, zPos])
        rotate([xRot, yRot, zRot])
        linear_extrude(length)
        polygon([p1,p2,p3]);
    }
    module subpart_nub(){
        xSize = lidNub_nub_x;
        ySize = lidNub_nub_y;
        zSize = lidNub_nub_z;

        xPos = 0;
        yPos = -(lid_y/2 - Wall_Thickness/2);
        zPos = lid_z;

        translate([xPos,yPos,zPos])
        roundedCylinder([xSize, ySize, zSize]);
    }
    module subpart_exraNearNub(){
        xSize = lidNub_tab_x;
        ySize = lidNub_tab_y;
        zSize = lidNub_tab_z;

        xPos = 0;
        yPos = -(lid_y/2 + ySize/2);
        zPos = 0;

        shortEnd = xSize/2;
        longEnd = xSize/2 + ySize;
        yDist = ySize/2;
        p1 = [ shortEnd,-yDist];
        p2 = [-shortEnd,-yDist];
        p3 = [-longEnd,  yDist];
        p4 = [ longEnd,  yDist];

        translate([xPos, yPos, zPos])
        linear_extrude(zSize)
        polygon([p1,p2,p3,p4]);
    }
}

module part_tester_body(){
    difference(){
        subpart_body();
        subpart_centerCutout();
        subpart_nubCutout();
    }

    //Sub-parts that are used to make this part
    module subpart_body(){
        xSize = tester_body_ext_x;
        ySize = tester_body_ext_y;
        zSize = tester_body_ext_z;

        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_centerCutout(){
        xSize = tester_body_int_x;
        ySize = tester_body_int_y;
        zSize = tester_body_int_z+eps;
        xPos = 0;
        yPos = 0;
        zPos = -eps/2;

        translate([xPos,yPos,zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
    module subpart_nubCutout(){
        xSize = lidNub_socket_x;
        ySize = lidNub_socket_y;
        zSize = lidNub_socket_z;

        xPos = 0;
        yPos = -(tester_body_ext_y/2 - Wall_Thickness/2);
        zPos = tester_body_ext_z;

        translate([xPos,yPos,zPos])
        roundedCylinder([xSize, ySize, zSize]);
    }
}

//Storage box, width is determined by how many you want to fit into storage
module part_storageBox(howManyBoxesFit){
    gapBetweenBoxes = Storage_Gap * (howManyBoxesFit - 1);
    widthOfEachBox_ext = (sSect_x/howManyBoxesFit) - gapBetweenBoxes;

    difference(){
        subpart_box();
        subpart_cutout();
    }

    module subpart_box(){
        xSize = widthOfEachBox_ext;
        ySize = sSect_y;
        zSize = sSect_z;

        cubeZAligned([xSize, ySize, zSize]);
    }

    module subpart_cutout(){
        xSize = widthOfEachBox_ext - (Storage_Container_Thickness*2);
        ySize = sSect_y - (Storage_Container_Thickness*2);
        zSize = sSect_z - Storage_Container_Thickness + eps;
        xPos = 0;
        yPos = 0;
        zPos = Storage_Container_Thickness;

        translate([xPos,yPos,zPos])
        cubeZAligned([xSize, ySize, zSize]);
    }
}

//////////////////////////////////////////////////
// Helpers and functions
//////////////////////////////////////////////////

//Make cube centered on x/y but with the base aligned to z=0
//Makes aligning stuff easier IMO
module cubeZAligned(size){
    linear_extrude(size.z)
    square([size.x, size.y], center=true);
}

module cubeYTopAligned(size){
    translate([0,-size.y/2,-size.z])
    linear_extrude(size.z)
    square([size.x, size.y], center=true);
}

// Creates a tube with rounded edges by connecting 2 spheres together
module roundedCylinder(size){
    sphereOffset = size.y/2 - size.x/2;

    hull(){
        translate([sphereOffset,0,0])
        resize([size.y, size.y, size.z])
        sphere(d=0.1);

        translate([-sphereOffset,0,0])
        resize([size.y, size.y, size.z])
        sphere(d=0.1);
    }
}

```


[User]
I need a parametric OpenSCAD design for a cable management system that can be attached to a desk or other surface. I'd like it to be highly customizable with different mounting and cable-securing options.

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

**Mounting Options:**

*   **Magnetic:** I want the option to use magnets for mounting. This should have three sub-options:
    *   **Pressed-in:** Circular magnets pressed into a recess on the bottom, maybe with some glue for extra security.
    *   **Side-Insert:** A slot or opening to insert a magnet from the side, secured with something like hot glue.
    *   **Screw-Fixed:** A countersunk bore for a screw to hold a magnet with a pre-made hole in the center (e.g., a countersunk magnet). It would need space for a nut inside the base.
*   **Screw:** A simple, centered screw hole to attach the guide directly to the surface.
*   **Flat Bottom:** A completely flat bottom so I can use adhesive tape or glue.

**Cable Securing Options:**

*   **Rings:** A series of intertwined, C-shaped rings or hooks, something like a cable comb but attached to a base. The rings should have a gap for inserting cables.
*   **Ties/Tape:** A slot or opening designed to thread Velcro cable ties or similar through.
*   **Foldback Clamps:** A simple dock or holder to attach standard foldback clamps.

**General Parameters:**

*   Base width, depth and height, all of which should be adjustable, possibly with auto calculation based on other dimensions or an option for manual override. There should be an option to make the top part of the base a different depth than the bottom part using a percentage value.
*   A height for the mid layer (between the magnets or screws and the bottom of the cable fastening area).
*   A number of magnet placement options (e.g. 1, 2, 3 for pressed in magnets).
*   A parameter for choosing the screw size (e.g. M3, M4, M5).  This should automatically adjust the screw hole and nut recess.
*   Some printer-dependent adjustment value to add to the diameter of the screw and magnet cutouts to allow for tolerances.
*   The ability to specify a diameter for any magnets.
*   Thickness for the magnets
*   Optionally rounded base to attach to round table legs.

**Specific Parameters (vary by fastening type):**

*   **Rings:**
    *   Ring thickness
    *   Inner diameter of the rings (cable space)
    *   Width of a single ring
    *   Space between the rings
*   **Ties/Tape:**
    *   Width of the tie opening
    *   Thickness of the tie opening
    *  Tie offset relative to the top of the base
    *   A rounding diameter for the tie slot.
*  **Clamps:**
    *   Thickness of clamp dock at the bottom
    *   Thickness of clamp dock at the top
    *   Height of clamp dock

I'm hoping you can generate a set of .scad files that allows for this level of customization. I'm not requesting code for any external libraries, but I'd like you to use the `BOSL` library.

[Assistant]
TableCableGuide.scad:
```
/* [Hidden] */
$fn = 100; // Set the number of facets for the polygon

// some defs
screw_diameters = [3, 4, 5];
screw_nut_widths = [5.5, 7, 8];
nut_thickness = [2.4, 3.2, 4]; // DIN 934

/*

  Mounting options: How should the cable guide be attached to the table or surface:
    - a magnet with countersunk bore is screwed on the cable guide bottom (counter nut inside cable guide)
    - one or more round magnets are pressed/glued into the cable guide bottom
    - a magnet is inserted sideways into an opening of the cable guide (less magnetic force)
    - table guide is screwed to surface (of course damaging it)
    - table guide has flat bottom and is attached with adhesive tape
*/

/* [General settings] */

// Type of cable guide
guide_type = "tie"; // [rings:Rings, tie:Velcro tape/cable ties, clamp:(Foldback) clamp]

// Mounting options
mounting_type = "magnetPressed"; // [magnetScrew:Magnetic - Round magnet with countersunk hole + secured with screw, magnetInsert:Magnetic - Magnet is inserted from side opening and secured with hot glue, magnetPressed: Magnetic - round magnet pressed in on bottom optionally secured with glue, screw:Screw - a single centered screw, flat:Flat Bottom - using glue or adhesive tape)]

// Width of base (in cable direction), -1 for auto width
w_base_common = 25;

// Depth of base, -1 for auto
d_base_common = 15;

// Height of base
h_base_common = 5;

// Depth of base at top as percentage of bottom depth
d_base_common_top_ratio_perc = 50;

// Height of mid layer  (above magnet/screw layer)
h_base_mid = 0.5;

/* [Settings for rings type] */
thn_rings = 2.5; // 2.5;

// Inner diameter of rings (space for cables)
d_guide_inner = 10;    // [10:60]
// w_guide_ring = 10;      // [4:20]

// Width of a single ring
w_guide_ring = 10;      // [4:20]

// Space between rings. The thickest cable should fit through.
offset_guide_rings = 6.5;

/* [Settings for cable/velcro tie type] */

// Width cable tie / velcro tape
w_tie = 20;

// Thickness of cable tie / velcro tape
h_tie = 3.0;

// Tie offset relative to top of base
h_tie_top_offset = 1.5;

// Rounding of tie slit
d_tie_rounding = 30.0;

/* [Settings for (foldback) clamp type] */

// dimension of clamp dock: thickness at bottom
d_clamp_dock_bottom = 3; // [2:6]

// dimension of clamp dock: thickness at top
d_clamp_dock_top = 5; // [2:6]

// dimension of clamp dock: height of dock
h_clamp_dock = 5; // [3:10]

/* [Overall dimensions] */

//

/* [Mount options] */

// Size of magnet or guide mounting screw. Nut dimensions are derived accordingly.
screw_type = 0; // [0: 3mm/M3, 1:4mm/M4, 2:5mm/M5]

// Printer dependend: extra diameter on screw cutout
cutout_adjust = 0.1;

/* [Magnets] */

// Diameter of magnet
d_magnet = 5.8;

// Height/thickness of magnet
h_base_magnet = 2.6;

// Number of magnets. Only valid for pressed-in variant. Ignored for other variants.
n_magnets = 1; // [1, 2]

/* [Rounded mounting surface] */

// rounding diameter, e.g. round table foot
d_surface_rounding = 0; // 60;

// * round 14mm magnet with countersunk bore in the middle *
/*
d_magnet = 14;
h_base_magnet = 4.8;
h_base_mid = 0.8;
*/

// * round 20mm magnet with countersunk bore in the middle *
/*
d_magnet = 20;
h_base_magnet = 3.7;
h_base_mid = 1.5;
*/

// * round 10mm cylinder magnet *
/*
d_magnet = 10;
h_base_magnet = 4.9;
h_base_mid = 1.8;
*/

/* screw only: height of layer below screw head */
h_base_screw = 0.5;

/* [Hidden] */
// computed values

d_screw = screw_diameters[screw_type];
h_screw_head = nut_thickness[screw_type];

// bottom size
/*
if (mounting_type == "screw") {
    h_base_bottom = h_base_screw;
} else if (mounting_type == "flat") {
    h_base_bottom = h_base_screw;
} else {

}
*/
d_base_top = d_base_common * d_base_common_top_ratio_perc / 100.0;
h_base_bottom = mounting_type == "screw" ? h_base_screw : h_base_magnet;
h_base_bottom_magnets_or_screw = h_base_bottom + nut_thickness[screw_type] + h_base_mid;
h_base_bottom_flat = h_base_common;
h_base_bottom_with_mount = mounting_type == "flat" ? h_base_bottom_flat : h_base_bottom_magnets_or_screw;
h_base_insert = mounting_type == "magnetInsert" ? 1 : 0;

function offset_surface_rounding(d_base) = sqrt((d_surface_rounding*d_surface_rounding/4) - (d_base*d_base/4));
function h_base_surface_rounding(d_base) = d_surface_rounding > 0 ? d_surface_rounding/2 - offset_surface_rounding(d_base) : 0;

if (guide_type == "rings" ) {
    guide_type_rings();
} else {
    guide_type_tie_or_clamp();
}

module guide_type_rings()
{
    // w_base = w_guide_ring * 2 + offset_guide_rings;
    d_rings = d_guide_inner / 2;

    d_magnet_min = d_magnet + 2;
    if (d_base < d_magnet_min || w_base < d_magnet_min) {
        echo("WARNING: base dimensions too small for magnet: depth is adjusted");
    }

    // l_magnet = d_magnet + 2 * 3;
    // l_base = l_magnet > l_rings ? l_magnet : l_rings;
    w_base = w_base_common > 0 ? w_base_common : (2 * w_guide_ring + offset_guide_rings);
    d_base = d_base_common > d_magnet_min ? d_base_common : d_magnet_min;
    echo("Dimensions: WxD", w_base, d_base);

    h_base_rounding = h_base_surface_rounding(d_base);
    echo("Extra base height for rounding: ", h_base_rounding);

    h_base = h_base_bottom_with_mount + ((d_surface_rounding > 0) ? h_base_rounding : 0) + h_base_insert;

    difference() {
        union() {
            difference() {
                hull() {
                    translate([-d_base/2, -w_base/2, 0])
                        if (mounting_type == "magnetScrew" || mounting_type == "magnetInsert" || mounting_type == "magnetPressed") {  // magnet with screw
                            cube([d_base, w_base, h_base_magnet + h_base_mid + h_base_rounding + h_base_insert]);
                        } else if (mounting_type == "screw" || mounting_type == "flat") {
                            cube([d_base, w_base, h_base_screw]);
                        }

                    translate([-d_rings/2-1, -w_base/2, 0])
                        cube([d_rings+2, w_base, h_base]);
                }

                /*
                    cut holes for magnets
                */
                magnets(w_base, d_base, h_base);
            }

            /*
                add cable guide rings
            */
            translate([0, -w_guide_ring/2 - offset_guide_rings/2, h_base - thn_rings])
                cable_guide_arc();

            translate([0, w_guide_ring/2 + offset_guide_rings/2, h_base - thn_rings])
                rotate([0, 0, 180])
                    cable_guide_arc();
        }

        /*
            cut holes for screw or nut
        */
        if (mounting_type == "magnetScrew") {  // magnet with screw
            translate([0, 0, h_base - nut_thickness[screw_type]])
                hex_nut(nut_thickness[screw_type]*2); // use extra height to cut thru arcs
        } else if (mounting_type == "screw") { // countersunk screw
            cylinder(h_base*3, d=d_screw+cutout_adjust, center=true);
            translate([0, 0, h_base - h_screw_head + 0.1])
                cylinder(h_screw_head+0.1, d1=d_screw, d2=d_screw*2+cutout_adjust);
        }

        if (d_surface_rounding > 0) {
            rounded_surface(w_base, d_base);
        }

    }
}

module guide_type_tie_or_clamp() {

    // w_base = w_base_tie;

    d_magnet_min = d_magnet + 2;
    if (d_base < d_magnet_min || w_base < d_magnet_min) {
        echo("WARNING: base dimensions too small for magnet");
    }

    w_base = w_base_common > d_magnet_min ? w_base_common : d_magnet_min;
    d_base = d_base_common > d_magnet_min ? d_base_common : d_magnet_min;
    echo("Dimensions: WxD", w_base, d_base);

    h_base_rounding = h_base_surface_rounding(d_base);
    echo("Extra base height for rounding: ", h_base_rounding);

    h_base_extra = h_base_insert + h_tie/2;
    h_base = h_base_bottom_with_mount + ((d_surface_rounding > 0) ? h_base_rounding : 0) + h_base_extra;

    difference() {
        union() {
            difference() {
                hull() {
                    translate([-d_base/2, -w_base/2, 0])
                        if (mounting_type == "magnetScrew" || mounting_type == "magnetInsert" || mounting_type == "magnetPressed") {  // magnet with screw
                            cube([d_base, w_base, h_base_magnet + h_base_mid + h_base_rounding  + h_base_extra]);
                        } else if (mounting_type == "screw" || mounting_type == "flat") {
                            cube([d_base, w_base, h_base_screw]);
                        }

                    // upper(smaller) part of base
                    translate([-d_base_top/2, -w_base/2, 0])
                        cube([d_base_top, w_base, h_base]);
                }

                /*
                    cut holes for magnets
                */
                magnets(w_base, d_base, h_base);

                if (guide_type == "tie") {
                    /*
                        cut opening for tie
                    */
                    // translate([0, 0, d_tie_rounding/2 + h_base_magnet + h_base_mid - 1 + h_base_rounding + h_base_extra]) {
                    translate([0, 0, d_tie_rounding/2 + h_base - h_tie - h_tie_top_offset]) {
                        rotate([90,0,0]) {
                            difference() {
                                rounding_factor = 10;
                                cylinder(w_tie, d=d_tie_rounding, center=true);
                                cylinder(w_tie*2, d=d_tie_rounding - h_tie, center=true);
                            }
                        }
                    }
                }
            }

            if (guide_type == "clamp") {
                echo("Clamp");
                scale = d_clamp_dock_top / d_clamp_dock_bottom;
                translate([0, 0, h_base])
                    linear_extrude(h_clamp_dock, scale=[scale,1])
                        square([d_clamp_dock_bottom, w_base], center=true);

            }

        }

        /*
            cut holes for screw or nut
        */
        if (mounting_type == "magnetScrew") {  // magnet with screw
            translate([0, 0, h_base_bottom + 1])
                hex_nut(nut_thickness[screw_type]*20); // use extra height to cut thru arcs
        } else if (mounting_type == "screw") { // countersunk screw
            d_screw_head = d_screw * 2.5;
            cylinder(h_base*3, d=d_screw+cutout_adjust, center=true);
            translate([0, 0, h_base_screw + h_screw_head + 0.1])
                cylinder(h_base*3, d=d_screw_head+cutout_adjust);
            translate([0, 0, h_base_screw + 0.1])
                cylinder(h_screw_head+0.1, d1=d_screw, d2=d_screw_head+cutout_adjust);
        }

        if (d_surface_rounding > 0) {
            rounded_surface(w_base, d_base);
        }
    }

}

module magnets(w_base, d_base, h_base) {
    // in case of rounding some extra height needs to be added to base and magnet cutouts
    // default height is in the middle of rounding - edges of base are higher
    h_base_magnet_rounding = h_base_surface_rounding(d_base);
    echo("Extra rounding height for magnet cutout: ", h_base_magnet_rounding);
    h_base_magnet_adj = h_base_magnet + h_base_magnet_rounding;
    if (mounting_type == "magnetScrew") {  // magnet with screw
        translate([0, 0, -0.1]) // only for better cut view in openscad editor
            cylinder(h_base_magnet_adj+0.1, d=d_magnet+cutout_adjust);

        cylinder(h_base*3, d=screw_diameters[screw_type]+cutout_adjust , center=true);
    } else if (mounting_type == "magnetInsert") {

      translate([0, 0, (h_base_magnet_adj+cutout_adjust)/2 + 1])
        cube([d_magnet+cutout_adjust, w_base*3, h_base_magnet_adj+cutout_adjust], center=true);
    } else if (mounting_type == "magnetPressed") {
        translate([0, 0, -0.01])
            if (n_magnets == 1) {
                cylinder(h_base_magnet_adj+0.1, d=d_magnet+cutout_adjust);
            } else {
                offset = w_base / (n_magnets);
                for (i = [1:n_magnets]) {
                    translate([0, i*offset-3*w_base/4, 0])
                        cylinder(h_base_magnet_adj+0.1, d=d_magnet+cutout_adjust);
                }
            }
    }
}

module rounded_surface(w_base, d_base) {

    if (d_surface_rounding > 0) {

        translate([0, 0, -offset_surface_rounding(d_base)])
            rotate([90, 0, 0])
                cylinder(2*w_base, d=d_surface_rounding, center=true);

    }

}

module cable_guide_arc()
{
    translate([0, w_guide_ring/2, d_guide_inner/2 + thn_rings]) {
        rotate([90, 0, 0]) {
            difference() {
                // Rotate the polygon around the Z axis to create the arc
                rotate_extrude(angle=270, convexity=0) {
                    square([d_guide_inner/2 + thn_rings, w_guide_ring]);
                }

                cylinder(w_guide_ring*3, d=d_guide_inner, center=true);
            }
        }
    }
}

module hex_nut(thn_screw_head) {

    //
    // w_head:
    //  M2: 4mm, M2.5: 5mm, M3: 5.5mm,  M4: 7mm,  M5: 8mm,
    //  M6: 10mm,  M8: 13mm,  M10: 17mm,  M12: 19mm
    //

    w_screw_head = screw_nut_widths[screw_type];

    r_head = w_screw_head / tan(60);
    r_slit = r_head+cutout_adjust/2;
    y_slit = r_slit * sin(60);
    w_slit = 0;
    linear_extrude(thn_screw_head)
        polygon([[0,-r_slit-w_slit/2], [-y_slit, -w_slit/2-r_slit/2],
                [-y_slit, w_slit/2+r_slit/2],[0,r_slit+w_slit/2],
                [y_slit, w_slit/2+r_slit/2],[y_slit, -w_slit/2-r_slit/2]]);
}

```


[User]
Hey there, I'm looking to generate some adapter plates using OpenSCAD, and I need some help getting the code set up. I've seen a great example, but I need something a bit more tailored to my needs. Here's what I'm aiming for:

**General Plate Parameters:**
*   I want to be able to specify the plate's overall width and length.
*   I also need to define the plate's height/thickness.
*   I want the corners to be rounded, and I need to be able to set that radius.

**Holes:**
*   I need to be able to add simple, circular holes at specific X and Y coordinates, each with a defined diameter. I'll need to add several of these.

**Screw Slots:**
*   I also need to add screw slots. These need to be configurable with:
    *   X and Y coordinates for their locations.
    *   A 'screw type' that references a lookup table to define the screw head diameter, depth, etc.
    *   An orientation setting (probably something simple like 0 for normal and 1 for reversed).
    *   I would like to be able to define more than one screw type (e.g. M3 and M4)

**Rectangular Cut-Outs:**
*   I need to be able to define rectangular cut-outs to reduce weight. I should be able to add several at specific locations with variable dimensions.

**Screw Definitions:**
*   I'll need a separate section to define each screw type's dimensions. This will include head diameter, head drop (countersink depth or standoff), head height, and screw leg diameter.

**Code Requirements:**
*   I need the code to be well-commented and easy to understand.
*   I'd like to be able to easily modify parameters.
*   I'd like to be able to save STL files.

Basically, I'm looking for a modular and parametric design with all those options. Could you create the necessary OpenSCAD files for me? I'm not picky about the details of the modules as long as everything is configurable. I don't need any external libraries for this.

[Assistant]
customizable-adapter-plate.scad:
```
/* Customizable Universal Adapter Plate
   written by torwan @ March 3rd, 2024, v1.1

   Description / Hints:
   - See more details on https://www.thingiverse.com/thing:6503664
   - All dimensions in [mm].
   - You can specify width, length, height and corner rounding radius
     of your adapter plate.
   - You can add infinite number of simple holes, screw slots
     and simple rectangular cut-outs to your adapter plate.
   - You can define dimensions of more than one screw slot type.
   - You can define orientation of the screw slots.
   - To generate STL file, render the object using F6 and save it
     using F7.
   - $fn controls number of a circle fragments. I recommend to use
     $fn = 50 or more.
*/

// ----------- Model general parameters ----------
/* [Model general parameters] */

// Plate width
plate_width = 105.0;

// Plate length
plate_length = 155.0;

// Plate height
plate_height = 5.0;

// Plate rounding radius (corners)
rounding_radius = 3;

// Coordinates of the holes (X1, Y1, DIA1, ..., Xn, Ym, DIAn)
holes = [8.0, 14.0, 5.5,  8.0, 34.0, 5.5,  147.0, 14.0, 5.5,  147.0, 34.0, 5.5,  37.0, 73.0, 4.0,  37.0, 93.0, 4.0,  112.0, 73.0, 4.0,  112.0, 93.0, 4.0];

// Coordinates of the screw holes (X1, Y1, SCREW_TYPE1, SCREW_ORIENT1, ..., Xn, Yn, SCREW_TYPEn, SCREW_ORIENTn
screw_holes = [90.0, 48, 1, 1,  122.0, 48.0, 1, 1];

// Coordinates of the rectangular cuts (X1, Y1, SIZE_X1, SIZE_Y1, ..., Xn, Ym, SIZE_Xn, SIZE_Yn)
rect_cuts = [22, 14, 110, 20, 44.5, 73, 60, 20, 15, 43, 60, 20, 8, 70, 20, 27, 121, 70, 25, 27];

// ----------- Screw-holes dimensions ------------
/* [Screw-holes dimensions] */

// Vector with screw holes (HEAD_DIA1, HEAD_DROP1, HEAD_H1, HEAD_LEG_DIA1, ..., HEAD_DIAn, HEAD_DROPn, HEAD_Hn, HEAD_LEG_DIAn
screw_holes_types = [11.2, 0.0, 4.8, 6.2,  8.2, 0.2, 2.6, 4.2];

// ------------ Supporting parameters ------------
/* [Supporting parameters] */
// Number of a circle fragments
$fn = 50;

// ------------- Helping parameters --------------
// (do not modify below parameters)
cc = 0.01;
rounding_radius_c = rounding_radius - cc;
// --------------- GENERATION AREA ---------------
/* [GENERATION AREA] */

show_adapter();

// ------------------ MODULES --------------------
// (you do not need to touch anything below unless you
// know what you do :) )

module show_adapter()
{
    difference()
    {
        plate_main_body();
        make_holes();
        make_screw_nests();
        make_rectangular_cuts();
    }
}

module plate_main_body()
{
    translate([rounding_radius_c, rounding_radius_c, 0])
        linear_extrude(plate_height)
            offset(r = rounding_radius_c)
                square([plate_length-2*rounding_radius_c,
                        plate_width-2*rounding_radius_c]);
}

module make_holes()
{
    no_holes = len(holes)/3;
    for (idx = [0:1:no_holes-1])
        translate([holes[idx*3], holes[idx*3+1], -cc/2])
            cylinder(d = holes[idx*3+2], h = plate_height+cc);
}

module make_screw_nests()
{
    no_screws = len(screw_holes)/4;
    for (idx = [0:1:no_screws-1])
        translate([screw_holes[idx*4], screw_holes[idx*4+1], -cc])
        {
            selected_screw_type = screw_holes[idx*4+2];
            screw_type_idx = (selected_screw_type-1)*4;
                translate([0, 0, cc+ plate_height*screw_holes[idx*4+3]])
                rotate([180*screw_holes[idx*4+3], 0, 0])
                screw_nest(head_dia = screw_holes_types[screw_type_idx],
                           head_drop = screw_holes_types[screw_type_idx+1],
                           screw_head_h = screw_holes_types[screw_type_idx+2],
                           total_length = plate_height+cc,
                           screw_leg_dia = screw_holes_types[screw_type_idx+3]);
        }
}

module make_rectangular_cuts()
{
    no_cuts = len(rect_cuts)/4;
    for (idx = [0:1:no_cuts-1])
        translate([rect_cuts[idx*4], rect_cuts[idx*4+1], -cc/2])
            cube([rect_cuts[idx*4+2], rect_cuts[idx*4+3], plate_height+cc]);
}

module screw_nest(head_dia = 6,
                  head_drop = 1,
                  screw_head_h = 3,
                  total_length = 10,
                  screw_leg_dia = 2) // Template for space for a mounting screw
{
    rotate_extrude(angle = 360)
        polygon([[0, 0],
                 [head_dia/2, -cc],
                 [head_dia/2, head_drop],
                 [screw_leg_dia/2, head_drop + screw_head_h],
                 [screw_leg_dia/2, total_length+cc],
                 [0, total_length+cc]
                ]);
}

```


[User]
Hey, I'm working on a project that needs some threaded rods, and I'm looking for a parametric solution in OpenSCAD. I want to be able to customize the threads pretty extensively, and I'd also like to handle both male and female threads, and even hollow male threads.

Specifically, I need a function that lets me specify:

*   The length of the threaded portion.
*   The radius of the thread (major diameter).
*   The pitch of the thread.
*   The thread angle (the angle from the valley to the crest and back down).
*   The minor diameter as a percentage of the pitch depth, or maybe the radius. It should be easy to adjust that.
*   The major diameter as a percentage of the pitch depth, or maybe the radius. Again, ease of adjustment is important.
*   A wall thickness that allows me to create solid male threads, hollow male threads (with a negative value for wall), and female threads (with a positive value for wall).
*   The number of facets in a full 360 degree circle - $fn should be settable.

Ideally, I'd like this to be a single function where all these parameters are named arguments, with some reasonable defaults. Something like `extrude_thread(length = 20, radius = 10, pitch = 2, pitchAngle = 60, innerPct = 0.25, outerPct = 0.125, wall = 0, fn = 60)` should be a valid use.

I'd like it if the code also included a few examples in the main module of how to use this function with various parameters.

I don't need to use any external libraries like MCAD, BOLTS, or similar. I just want a plain OpenSCAD file that works as described.

[Assistant]
threadedrod.scad:
```
/*
The extrude_thread() function can be used to generate parametric threaded parts.
All other functions are for support only and are not intended to be called.

extrude_thread() can take the following arguments.
Argument    Default Notes
length      1       Length of threaded part
radius      0.5     Distance from center to Major Diameter
pitch       0.1     Height of a single thread
pitchAngle  60      Angle from valley to crest to valley
innerPct    0.25    Minor Diameter as %
outerPct    0.125   Major Diameter as %
wall        0       Zero for solid male threads. Negative for male thread with wall thickness inside Minor Diameter. Positive for female thread with wall thickness outside Major Diameter.
fn          60      Number of faces in 360 degrees
*/

//Thread Length
tLength = 20; //[0:100]

//Thread Radius
tRadius = 10;//[5:30]

//Thread Pitch
tPitch = 2;//[1:10]

//Wall: Solid = 0; Hollow Male = Negative; Female = Positive
tWall = 0;//[-10:10]

//Example Thread - Comment out if included in another file.
//translate([25,0,0])
//extrude_thread(length = 20, radius = 10, pitch = 2, wall = -1);
//extrude_thread(length = 20, radius = 10, pitch = 2, wall = 1);
//translate([0,-25,0])
extrude_thread(length = tLength, radius = tRadius, pitch = tPitch, wall = tWall);

//Returns r if r is between inner bound i and outer bound o.
function r(i,r,o) = (r > o)?o:(r < i)?i:r;

//Calculated Pitch Depth based on Pitch Angle
function pitchDepth(p,pa) = (p * tan(90 - (pa/2))/2);

//Pitch Radius
function pr(r, a) = (r/2) * (1 +  cos(a));

//Cross Section Radius
function csr(r1,r2,a,o) = r1 + o - pr(r2,a);

//X and Y coordinates of Cross Section at angle a
function coords(r1,r2,a,i,o) = [
r(r1 + o - r2 + i, csr(r1,r2,a, o), r1) * cos(a),
r(r1 + o - r2 + i, csr(r1,r2,a, o), r1) * sin(a)
];

function ecoords(r1,r2,a,i,o,w) = [
r(r1 + i + o - r2, csr(r1,r2,a, o), (o > w/4)?r1+w/4:r1 +o) * cos(a),
r(r1 + i + o - r2, csr(r1,r2,a, o), (o > w/4)?r1+w/4:r1 +o) * sin(a)
];

module cross_section(r = 1, pr = 0.05, inner = 0, outer = 0, w = 0, n = 60){
    points = [for(i=[0:(360/n):360])
        coords(r,pr,i, inner, outer)];
    if(w < 0){
        difference(){
            polygon(points);
            circle(r = r - pr + w, $fn = n);
            }
        }
    else if (w == 0){
        polygon(points);
        }
    if(w > 0){
        points = [for(i=[0:(360/n):360]) ecoords(r,pr,i, inner, outer, w)];
        difference(){
            circle(r = r + w, $fn = n);
            polygon(points);
            }
        }
}

module extrude_thread(length = 1, radius = 0.5, pitch = 0.1, pitchAngle = 60, innerPct = 0.25, outerPct = 0.125, wall = 0, fn = 60){
    threadDepth = pitchDepth(pitch, pitchAngle);
    linear_extrude(height = length, twist = -(length/pitch) * 360, slices = (length/pitch) * fn)
    cross_section(r = radius, pr = threadDepth, inner = threadDepth*innerPct, outer = threadDepth*outerPct, w = wall, n = fn);
}
```


[User]
I need a stencil generator, and I want it to be pretty customizable. I've been using OpenSCAD for a while, so I'm comfortable with more advanced parameters. The basic idea is that I need a rectangle that's cut out by text. This will be for spray painting, so I also need a good-sized border around the text to help catch overspray.

Here's what I'm thinking for features and parameters:

*   **Text:**
    *   A string for the text to display.
    *   A font selector (I'd like to have several pre-selected fonts to pick from, like "Arial", "Times New Roman", "Impact", "Black Ops One", "Stardos Stencil" and "Big Shoulders Stencil Text", and ideally, an option to use any system font, though that might be too advanced).
    *   A text size parameter.

*   **Stencil Dimensions:**
    *   Overall width and height of the rectangular stencil area.
    *   Thickness of the stencil material.
    *   An offset parameter that will move the text up or down relative to the center of the stencil.
    *   A border width parameter that will control how much material is around the text cutout.

*   **Output:**
    *   I'd like to be able to generate the 2D stencil cut-out design.
    *   Ideally it should use standard CSG difference for cutouts.

Also, I have no preference for libraries. Any of the popular ones are fine like MCAD, BOSL, BOSL2, or something else.

Could you help me create that?

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

/* [Stencil] */

// font variables
text_to_display = "Stencil Customizer";

font_name = "Black Ops One"; // [Allerta Stencil, Black Ops One, Big Shoulders Stencil Display, Big Shoulders Stencil Text, Codystar, Emblema One, Keania One, Kenia, Libre Barcode 128, Palette Mosaic, Plaster, Raleway Dots, Rubik 80s Fade, Rubik Marker Hatch, Rubik Maze, Saira Stencil One, Sirin Stencil, Stardos Stencil, Stick No Bills, Wallpoet]

text_size = 20; // [100]

// stencil variables

verticle_offset = 0; // [-100:100]

width = 400; // [1000]

height = 50; // [1000]

thickness = 1; // [0.5:0.5:3]

// Create the stencil text
module create_text() {
    linear_extrude(thickness + 2)
    text(text_to_display, size = text_size, font = font_name, halign = "center", valign = "center");
}

// Calculate the position for centering
x_offset = -width / 2;
y_offset = -height / 2;

// Create the stencil and subract the text
module create_stencil()
{
    difference() {
        translate([x_offset, y_offset, 0])
        {
            cube([width, height, thickness]);
        }
        translate([0, verticle_offset, -1])
        {
            create_text();
        }
    }
}

// Display the result
create_stencil();

```


[User]
Hey there, I'm looking to design a custom drain grate using OpenSCAD, and I need some help with the code. I have a specific design in mind, and it's kind of a remix of an existing design, but with a key addition.

Essentially, I need a circular drain grate with spokes, but also with drainage gaps on the base disk. Here are the specific parameters I'm thinking of:

*   **`thk`**: Thickness of the drain cover (e.g., 2.25mm).
*   **`top_diameter`**: Diameter of the top of the grate (e.g., 24mm).
*   **`bottom_diameter`**: Diameter of the bottom of the grate (e.g., 38mm).
*   **`top_height`**: Height of the spoke portion of the grate (e.g., 24mm).
*   **`bottom_height`**: Height of the base (e.g., 10mm).
*   **`base_extension`**: Width of the lip on the base (e.g., 4mm).
*   **`num_spokes`**: Number of spokes (e.g., 18).
*   **`num_drain_gaps`**: Number of drain gaps around the base's perimeter (e.g., 6). This is crucial!

The spokes should connect the top diameter to the bottom diameter, with the height being `top_height`. I need a circular top with a sort of cut-out, a solid base with the specified lip, and the `num_drain_gaps` should be spaced as evenly as possible *between* the spokes, not directly under them. There should also be a solid base underneath with the specified `bottom_height`, and this should be inset from the lip (with a hole the diameter of `bottom_diameter`).

I'm thinking that, the `num_drain_gaps` variable should be a key parameter to control the number of gaps around the base’s perimeter, and I'd like the code to calculate the offset and width of the gaps to ensure they align roughly between spokes. It seems like using divisors of the number of spokes for the number of drain gaps would be the best way to accomplish this.

I don't need any special libraries or anything, just a straightforward OpenSCAD file. I'd really appreciate it if you could generate the SCAD code for this. Let me know if you need any more info.

[Assistant]
Drain_Grate_custom_with_drain_gaps.scad:
```
// Thickness of drain cover
thk = 2.25;

// Diameter of top of grate
top_diameter = 24;

// Diameter of bottom of grate
bottom_diameter = 38;

// Grate height
top_height = 24;

// Base height
bottom_height=10;

// Base lip width
base_extension = 4;

// Number of spokes
num_spokes = 18;

// Number of gaps on the base perimeter for flat level water draining.
// This is desirable if your atrium grate does not sit flush.
// Try small divisors of num_spokes to maximize chance of gaps sitting squarely between spokes.
num_drain_gaps = 6;

// Derived constants. Feel free to tweak these if gaps are not aligned with spokes boundaries.
gap_offset = 360/(num_drain_gaps+1.25);
gap_width = (bottom_diameter+base_extension*2) / (90 / num_spokes);

$fn = 80;

// Individual spoke for drain cover (to be repeated and rotated)
module spoke() {
	translate( [bottom_diameter/2, thk/2, 0] )
		rotate( [90, 0, 0] )
			linear_extrude( height=thk )
                // Use -.1 to nudge down
				polygon(
                    points=[
                        [-thk, -.1],
                        [-bottom_diameter/2+top_diameter/2-thk/2, top_height-thk],
                        [-bottom_diameter/2+top_diameter/2, top_height],
                        // Use -.1 to nudge down
                        [0, -.1]
                    ]
                );
}

module round_spokes() {
    // create spokes
	for (snum=[1:num_spokes]) {
		rotate( [0, 0, snum*(360/num_spokes)] ) {
        	spoke(top_diameter, bottom_diameter, top_height);
        }
	}
}

module top() {
	num_top_spokes = ceil(num_spokes/2);
	translate( [0, 0, top_height-thk] ) {
		difference() {
			cylinder( d=top_diameter, h=thk );
			cylinder( d=top_diameter-thk*2, h=thk );
		}

		union() {
			for (snum=[1:num_top_spokes]) {
				rotate( [0, 0, snum*(360/num_top_spokes)] )
					translate( [0, -thk/2, 0] )
							cube( [top_diameter/2, thk, thk] );
			}
			cylinder( d=top_diameter/3, h=thk );
		}

	}
}

module donut() {
	// Note - the 0.1 fudge is to make this 2-Manifold compliant
	translate( [0, 0, -thk-0.1] ) {
		difference() {
			cylinder( d=bottom_diameter+base_extension*2, h=thk+0.1 );
			cylinder( d=bottom_diameter-thk*2, h=thk+0.1 );
		}
	}
}

module donut_gaps() {
    for (i = [1:num_drain_gaps]) {
        // Cryptic: number of "gaps" between spokes is always 1 + number of spokes.
        // To avoid the gap landing right under a spoke, you add the 360/(num_gaps+1) offset to ensure the gap is centered between spokes.
        // This is mostly effective when num_drain_gaps is a small divisor of num_spokes.
        rotate([0, 0, i*(360/num_drain_gaps) + gap_offset]) {
            translate([(bottom_diameter+base_extension*2)/4, 0, -(thk+0.1)/2]) {
                cube([(bottom_diameter+base_extension*2)/2, gap_width/2, thk+0.1], center=true);
            }

        }
    }
}

module bottom() {
	translate( [0, 0, -thk-bottom_height] ) {
		difference() {
			cylinder( d=bottom_diameter, h=bottom_height );
			cylinder( d=bottom_diameter-thk*2, h=bottom_height );
		}
	}
}

// Drain cover
module drain() {

	// create spokes
	round_spokes();

	// top
	top();

	// solid base with drain gaps
    difference() {
        donut();
        donut_gaps();
    }

	// solid bottom
	bottom();
}

drain();

```


[User]
I'm looking to create a customizable separator, and I've seen some examples that look pretty close to what I need. I'd like to be able to choose between a rectangular or circular shape, and I need to be able to specify the dimensions for each shape. For the rectangular separator, I need control over the width, length, height, and corner rounding radius. For the circular one, I'll need to specify the diameter and height.

Additionally, I need to add holes to the separator. I'd like to be able to specify the diameter of these holes and the distance between them. If I set the distance to zero, then only a single hole should be created. It would be helpful if I could easily generate STL files from this design for 3D printing. I want it to be easily customizable using OpenSCAD so I can change the parameters as needed. It would be preferable if you could provide me with a single `.scad` file to do this.

[Assistant]
customizable-basic-separator.scad:
```
/* Customizable Basic Separator
   written by torwan @ March 8th, 2024

   Description / Hints:
   - See more details on https://www.thingiverse.com/thing:6514757
   - You can generate rectangular and circular separators. Choice is made
     via "separator_type" parameter.
   - Separators can have holes (one or two). Distance between the holes is
     defined via "holes_distance" parameter. If "zero", then only one hole
     will be generated.
   - All dimensions in [mm].
   - To generate STL file, render the object using F6 and save it
     using F7.
   - $fn controls number of a circle fragments. I recommend to use
     $fn = 50 or more.
*/

// -------------- What to generate? --------------
/* [What to generate?] */

// Type of separator to generate: 0 - rectangular, 1 - circular
separator_type = 0; //[0, 1]

// ------- Rectangular separator dimensions ------
/* [Rectangular separator dimensions] */

// Rectangular separator width
separator_width = 20.0;

// Rectangular separator length
separator_length = 50.0;

// Separator height
rect_separator_height = 5.0;

// Rectangular separator rounding radius (corners)
rounding_radius = 3.0;

// --------- Circular separator dimensions ------
/* [Circular separator dimensions] */

// Circular separator diameter
separator_diameter = 50;

// Circular separator height
circ_separator_height = 7.0;

// -------------- Holes dimensions ---------------
/* [Holes dimensions] */

// Distance between holes (zero means one hole)
holes_distance = 30.0;

// Hole diameter
hole_dia = 4.3;

// ------------ Supporting parameters ------------
/* [Supporting parameters] */
// Number of a circle fragments
$fn = 100;

// ------------- Helping parameters --------------
// (do not modify below parameters)
/* [Hidden] */
cc = 0.01;
rounding_radius_c = rounding_radius - cc;
separator_height = separator_type ? circ_separator_height : rect_separator_height;

// --------------- GENERATION AREA ---------------
/* [GENERATION AREA] */

show_separator();

// ------------------ MODULES --------------------
// (you do not need to touch anything below unless you
// know what you do :) )

module show_separator()
{
    difference()
    {   union()
        {
            if (separator_type==0) show_rectangular_separator();
            if (separator_type==1) show_circular_separator();
        }
         make_holes();
    }
}

module show_rectangular_separator()
{
    translate([rounding_radius_c-separator_length/2,
               rounding_radius_c-separator_width/2,
               0])
        linear_extrude(separator_height)
            offset(r = rounding_radius_c)
                square([separator_length-2*rounding_radius_c,
                        separator_width-2*rounding_radius_c]);
}

module show_circular_separator()
{
    cylinder(d = separator_diameter, h = separator_height);
}

module make_holes()
{
    for (idx = [-holes_distance/2, holes_distance/2+cc/2])
        translate([idx, 0, -cc/2])
        {
            cylinder(d = hole_dia, h = separator_height + cc);
        }
}
```


[User]
Hey there! I'm looking to create a parametric design for a rotating drawer system, specifically one designed to hold filament spools. I'd like it to have a transparent acetate viewing window on each drawer so I can see what's inside. The design will be 3D printed, and I'm planning on using either ABS or PLA.

Here's a breakdown of the features I'd like:

*   **Rotating Drawer System:** The drawers will rotate around a central axis, kind of like a lazy susan.
*   **Transparent Viewers:** Each drawer needs a slot to hold a thin acetate sheet, creating a window to see the spool inside.
*   **Parametric Design:** I need to be able to customize dimensions and aspects of the design through parameters.
*   **Threaded Rod Joint:** I intend to use M4 threaded rod (or possibly fiberglass sticks) as the joint for the rotation, secured with M4 nuts and washers.

Here are the parameters I think I'll need:

*   **Tolerances:**
    *   `clearance`: A small gap between parts for assembly (e.g., 0.3mm).
    *   `clearance_angle`: A small gap in angles for assembly (e.g., 0.1mm).
*   **Wall Parameters:**
    *   `wall_thickness`: Thickness of the walls (between 0.8 and 2mm).
    *   `bottom_thickness`: Thickness of the bottom of the drawer (between 0.1 and 1.2mm).
    *   `tear_height`: Height of the slot for the acetate sheet (e.g., 3mm).
    *   `tear_thickness`: Thickness of the acetate slot walls (e.g., 0.7mm).
    *   `hinge_pin_diameter`: Diameter of the hole for the threaded rod (e.g., 4.8mm).
*   **Drawer Parameters:**
    *   `external_diameter`: External diameter of the filament reel.
    *   `internal_diameter`: Internal diameter of the filament reel.
    *   `height`: Internal height of the filament reel.
    *   `divisions`: Number of dividers inside the drawer (can be 0).
    *   `drawer_amount`: Quantity of drawers in the system (e.g., 4, 6, or 8).
*   **Options to Print:**
    *   `parts_to_draw`: An option to generate either just the drawer, just the frame that holds the drawers, both, both assembled, or a cutting jig for the acetate.

Could you help me generate a parametric OpenSCAD file (or files, if that's easier) for this? I'm imagining the drawers will be individual pieces and will connect to an upper frame. The drawers will need to rotate, so there will need to be space for the M4 rod and some hardware. I think that's about it for now. Let me know what you think!

[Assistant]
spool_drawer_acetate.scad:
```
// Parametric drawer for Filament Reels
// by ARamos ramos.alan@gmail.com July 2020
// remixed by Rogério Muinhoz 2024

/**
 * @name Drawer with transparent viewer
 * @category Printed
 * @using ABS or PLA
 * @using Transparent Acetate Sheet
 * @using M4 threaded bar or fiberglass sticks as joint
 * @using M4 nuts and washers
 * @using Empty filament reel
 */

// System constants.
C_CONSTANT = 0 + 0;

/* [Tolerances] */
// Clearance between walls for assembly
clearance=0.3;

// Clearance in angles for assembly
clearance_angle=0.1;

/* [Walls Parameters] */
// Walls thickness between 0.8 and 2
wall_thickness=0.8; // [0.8:0.1:2]

// Bottom thickness between 0.9 and 1.2
bottom_thickness=1; // [0.1:0.1:1.2]

// Height of the cut for the acetate
tear_height=3;

// Thickness of the cut for the acetate
tear_thickness=0.7; // 0.1

// Hole for the threaded rod to rotate the drawer
hinge_pin_diameter=4.8; // 0.1

/* [Drawer Parameters] */
// External diameter of the reel
external_diameter=189;

// Internal diameter of the reel
internal_diameter=83;

// Internal height of the reel
height=42;

// Number of divisions. Can be 0
divisions=1;

// Quantity of drawers
drawer_amount=4; // [4, 6, 8]

// Opening angle: 360 degrees divided by the number of drawers
opening_angle=floor(360/drawer_amount);

/* [Options to print] */
// Parts to be drawn
parts_to_draw=3; // [1:Drawer, 2:Upper Frame, 3:Both, 4:Both assembled, 5:Acetate cutting jig]

if (parts_to_draw==1)
	Drawer();

if (parts_to_draw==2)
	rotate([180,0,90])
		mirror([0,0,1])
			Frame();

if (parts_to_draw==3)
{
	Drawer();
	translate([10,10,0])
		rotate([180,0,90])
					mirror([0,0,1])
						Frame();
		}

		if (parts_to_draw==4)
		{
			Drawer();
			translate([0,0,height])
				mirror([0,0,1])
					Frame();
			%Acetate();
		}

		if (parts_to_draw==5)
			Acetate_Jig();

		// Tab on the top of the upper frame
		tab_height=0;

		module Drawer()
		{
			$fn=60;

			external_diameter=external_diameter/2;
			internal_diameter=external_diameter/2;

			zero_xy=internal_diameter;
			width=external_diameter-internal_diameter;
			height_front=height-bottom_thickness;
			vertical_tear_angle=2;
			hinge_ext_diam=hinge_pin_diameter+wall_thickness*2;

			intersection()
			{
				hull()
				{
					translate([external_diameter-hinge_ext_diam/2,hinge_ext_diam/2,0])				cylinder(d=hinge_ext_diam, h=height);
					translate([zero_xy,hinge_ext_diam/2,0])							cylinder(d=hinge_ext_diam, h=height);
					translate([hinge_ext_diam/2,zero_xy,0])							cylinder(d=hinge_ext_diam, h=height);
					translate([hinge_ext_diam/2,external_diameter+0.5,0])				cylinder(d=hinge_ext_diam, h=height);
					translate([external_diameter,external_diameter,0])	cylinder(d=hinge_ext_diam, h=height);
				}

				difference()
				{
					union()
					{
					// Back wall
						rotate_extrude(angle=opening_angle, convexity=10, $fn=100)
							translate([0,0,0])
								polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,height],[zero_xy,height]]);

						translate([0,0,bottom_thickness])
						{
					// Left front wall
							rotate([0,0,vertical_tear_angle])
							{
								rotate_extrude(angle=vertical_tear_angle+1, convexity=10, $fn=300)
									translate([width-wall_thickness,0,0])
										polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,height_front-tear_height-bottom_thickness-clearance],[zero_xy,height_front-tear_height-bottom_thickness-clearance]]);
		*						rotate_extrude(angle=vertical_tear_angle+1, convexity=10, $fn=300)
									translate([width-wall_thickness-wall_thickness-tear_thickness,0,0])
										polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,height_front-tear_height-bottom_thickness-clearance],[zero_xy,height_front-tear_height-bottom_thickness-clearance]]);
							}

					// Right front wall
							rotate([0,0,opening_angle-vertical_tear_angle])
							{
								rotate_extrude(angle=vertical_tear_angle, convexity=10, $fn=300)
									translate([width-wall_thickness,0,0])
										polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,height_front-tear_height-bottom_thickness-clearance],[zero_xy,height_front-tear_height-bottom_thickness-clearance]]);
		*						rotate_extrude(angle=vertical_tear_angle, convexity=10, $fn=300)
									translate([width-wall_thickness-wall_thickness-tear_thickness,0,0])
										polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,height_front-tear_height-bottom_thickness-clearance],[zero_xy,height_front-tear_height-bottom_thickness-clearance]]);
							}

					// Slot for acetate at the bottom
							rotate([0,0,vertical_tear_angle])
							{
								rotate_extrude(angle=opening_angle-vertical_tear_angle, convexity=10, $fn=300)
									translate([width-wall_thickness,0,0])
										polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,tear_height],[zero_xy,tear_height]]);
								rotate_extrude(angle=opening_angle-vertical_tear_angle, convexity=10, $fn=300)
									translate([width-wall_thickness-wall_thickness-tear_thickness,0,0])
										polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,tear_height],[zero_xy,tear_height]]);
							}
						}

					// Bottom
						rotate_extrude(angle=opening_angle, convexity=10, $fn=100)
							translate([width+internal_diameter,0,0])
								rotate([0,0,90])
									polygon(points=[[0,0],[bottom_thickness,0],[bottom_thickness,width],[0,width]]);

					// Left side
						translate([zero_xy+wall_thickness-clearance,0,0])
							cube([width-wall_thickness+clearance*2,wall_thickness,height]);

					// Right side
						rotate([0,0,opening_angle])
							mirror([0,1,0])
								translate([zero_xy+wall_thickness-clearance,0,0])
									cube([width-wall_thickness+clearance*2,wall_thickness,height]);

					// Dividers
						pa=opening_angle/(divisions+1);
						for (a=[pa:pa:opening_angle-pa])
							rotate([0,0,a])
								translate([zero_xy+wall_thickness-clearance,0,0])
									cube([width-wall_thickness*2-tear_thickness+clearance,wall_thickness,height-tear_height-bottom_thickness]);

					// Pull tab
						rotate([0,0,opening_angle-vertical_tear_angle])
							rotate_extrude(angle=vertical_tear_angle, convexity=10, $fn=300)
								translate([width-clearance/100,0,0])
									polygon(points=[[zero_xy,0],[zero_xy+wall_thickness*5,0],[zero_xy+wall_thickness*5,height],[zero_xy,height]]);

					// Guide pin
						translate([external_diameter-hinge_ext_diam/2,hinge_ext_diam/2,-clearance/10000])
							cylinder(d=hinge_ext_diam, h=height_front-tear_height-clearance);

					}

				// Guide pin
					translate([external_diameter-hinge_ext_diam/2,hinge_ext_diam/2,height-tear_height-bottom_thickness-clearance])
						cylinder(d=hinge_ext_diam+clearance, h=tear_height+bottom_thickness+clearance);

				// Guide pin
					translate([external_diameter-hinge_ext_diam/2,hinge_ext_diam/2,-clearance])
						cylinder(d=hinge_pin_diameter, h=height+clearance*2);
				}
			}
		}

		module Frame()
		{
			$fn=60;

			external_diameter=external_diameter/2;
			internal_diameter=external_diameter/2;

			zero_xy=internal_diameter;
			width=external_diameter-internal_diameter;
			bottom_width=wall_thickness*3+tear_thickness;
			height=tear_height+bottom_thickness;
			vertical_tear_angle=2;
			hinge_ext_diam=hinge_pin_diameter+wall_thickness*2;

			intersection()
			{
				hull()
				{
					translate([external_diameter-hinge_ext_diam/2,hinge_ext_diam/2,-tab_height])	cylinder(d=hinge_ext_diam, h=height+tab_height);
					translate([zero_xy,hinge_ext_diam/2,-tab_height])								cylinder(d=hinge_ext_diam, h=height+tab_height);
					translate([hinge_ext_diam/2,zero_xy,-tab_height])								cylinder(d=hinge_ext_diam, h=height+tab_height);
					translate([hinge_ext_diam/2,external_diameter+0.5,-tab_height])				cylinder(d=hinge_ext_diam, h=height+tab_height);
					translate([external_diameter,external_diameter,-tab_height])					cylinder(d=8, h=height+tab_height);
				}

				difference()
				{
					union()
					{
						translate([0,0,bottom_thickness])
						{
					// Slot for acetate at the bottom
							rotate_extrude(angle=opening_angle-clearance_angle*5, convexity=10, $fn=300)
							{
								translate([width-wall_thickness*3-tear_thickness,0,0])
									polygon(points=[[zero_xy,0],[zero_xy+wall_thickness*2,0],[zero_xy+wall_thickness*2,tear_height],[zero_xy,tear_height]]);
								translate([width-wall_thickness,0,0])
									polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,tear_height],[zero_xy,tear_height]]);
							}
						}

					// Bottom
						rotate_extrude(angle=opening_angle-clearance_angle*5, convexity=10, $fn=300)
							translate([width+internal_diameter,0,0])
								rotate([0,0,90])
									polygon(points=[[0,0],[bottom_thickness,0],[bottom_thickness,bottom_width],[0,bottom_width]]);
					// Upper tab

						if (tab_height>0)
							rotate([0,0,vertical_tear_angle])
							translate([0,0,-bottom_thickness-bottom_thickness])
								rotate_extrude(angle=opening_angle-vertical_tear_angle-clearance_angle, convexity=10, $fn=300)
										translate([width-wall_thickness,0,0])
											polygon(points=[[zero_xy,0],[zero_xy+wall_thickness,0],[zero_xy+wall_thickness,tab_height],[zero_xy,tab_height]]);

					// Guide pin
						translate([external_diameter-hinge_ext_diam/2,hinge_ext_diam/2,0])
							cylinder(d=hinge_ext_diam, h=tear_height+bottom_thickness);
					}
				// Guide pin
					translate([external_diameter-hinge_ext_diam/2,hinge_ext_diam/2,-clearance])
						cylinder(d=hinge_pin_diameter, h=tear_height+bottom_thickness+clearance*2);
				}
			}
		}

		module Acetate_Jig()
		{
			vertical_tear_angle=2;
			jig_height=height-bottom_thickness*2;
			jig_width=(PI/(360/(opening_angle-(vertical_tear_angle*3+2))))*(external_diameter-wall_thickness);
			jig_thickness=bottom_thickness*2;
			jig_border=bottom_thickness*10;

			font = "Calibri:style=Bold";

		//Height
			echo("Height: ",jig_height);
		//Width
			echo("Width: ",jig_width);

			//str("This is ",number,3," and that's it.")
			text= str("Dimensions: ",ceil(jig_height),"mm x ",round(jig_width),"mm");

			difference()
			{
				cube([jig_height,jig_width,jig_thickness]);
				translate([10,4,jig_thickness/2])
					rotate([0,0,90])
						linear_extrude(height = jig_thickness+clearance)
							text(text = "Acetate Cut Jig", font = font, size = 7);
				translate([18,4,jig_thickness/2])
					rotate([0,0,90])
						linear_extrude(height = jig_thickness+clearance)
							text(text = "by Alan Ramos", font = font, size = 7);
				translate([28,4,jig_thickness/2])
					rotate([0,0,90])
						linear_extrude(height = jig_thickness+clearance)
							text(text = text, font = font, size = 7);
			}
		}

		module Acetate()
		{
			$fn=60;

			external_diameter=external_diameter/2;
			internal_diameter=external_diameter/2;

			zero_xy=internal_diameter;
			width=external_diameter-internal_diameter;
			vertical_tear_angle=2;

			translate([0,0,bottom_thickness+clearance])
				rotate([0,0,vertical_tear_angle*2])
				rotate_extrude(angle=opening_angle-vertical_tear_angle*2+1, convexity=10, $fn=300)
					translate([width-wall_thickness-tear_thickness,0,0])
						polygon(points=[[zero_xy,0],[zero_xy++tear_thickness,0],[zero_xy+tear_thickness,height-tear_height-clearance*2],[zero_xy,height-tear_height-clearance*2]]);
		}

```


[User]
I'm looking for an OpenSCAD design for a customizable cable management clip, similar to the one I found on Thingiverse but I'd like it to be parametric. The basic idea is a series of clips that hold cables, and the key things I want to be able to adjust are:

*   The number of cables the clip is designed to hold.
*   The diameter of the cables being held.
*   The size of the gap in the clip that the cable is pushed through (the cable passage).
*   The thickness of the clip material itself.
*   The overall height of the clip.

I'm thinking it should be a series of these clips arranged in a line, with alternating 'top' and 'bottom' clips, if that makes sense. It's like a zig-zag pattern, with filler pieces between the clips.  I need the parametric design to be a single .scad file. I don't think any external libraries would be required.

[Assistant]
ParametrizableCableManager_V01.scad:
```
/* LICENSE

Parametrizable Cablemanager (https://www.thingiverse.com/thing:6519407) (c) 2024 by BTLBTL.
is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license
Date: 03 March 2024

To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/
*/

/* [Parameters] */

// number of cables to be clipped in
numCables = 3; // [1:10]

// the diameter of the cable to be clipped in [mm]
cableDiameter = 8; // [2:15]

// size of the cable passage to clip the cable in [mm]
cablePassage = 4; // [1:0.5:13]

// thickness of the clips in [mm], defines the outer diameter
thickness = 2; // [1:6]

// height of cable management clip in [mm]
height = 5; // [3:15]

module __Customizer_Limit__ () {}  // This actually works
$fn=64;

/* ---------------------------------------------------------------------------
 *
--------------------------------------------------------------------------- */
cableClipManagement(cableDiameter,cablePassage,numCables,thickness,height);

/* ---------------------------------------------------------------------------
cableClipManagement(....);

Parameters
    cableDiameter  --> the diameter of the cable to be clipped in [mm]
    cablePassage   --> size of the cable passage to clip the cable in [mm]
    numCables      --> number of cables to be clipped in
    thickness      --> thickness of the clips
    height         --> height of cable management clip
--------------------------------------------------------------------------- */
module cableClipManagement(cableDiameter, cablePassage,  numCables=3, thickness=2, height=5) {

    innerRadius = cableDiameter / 2;
    shift       = cableDiameter + thickness;

    for (count = [0:numCables-1]) {
        dx = count * cos(60) * shift;
        dy =         sin(60) * shift;
        if (count % 2 == 0) {
            // Upside clip
            translate([(count/2)* /* cos(0) * */ shift, 0,0]) {
                //color("blue",0.3)
                singleClip(cableDiameter, cablePassage, thickness, height);
            }
            // Downside filler
            if (numCables > 1) {
                dxFiller = (count+1) * cos(60) * shift;
                translate([dxFiller,-dy,0]) {
                    rotate(a=180, v=[0,0,1]) {
                        //color("purple",0.5)
                        clipFiller(cableDiameter,thickness,height);
                    }
                }
            }

        } else {
            // Downside clip
            //color("green",0.5)
            translate([dx,-dy,0]) {
                rotate(a=180, v=[0,0,1]) {
                    singleClip(cableDiameter, cablePassage, thickness, height);
                }
            }
            // Upside filler
            dxFiller = ((count-1)/2)* /* cos(0) * */ shift;
            translate([dxFiller, 0,0]) {
                //color("yellow",0.5)
                clipFiller(cableDiameter,thickness,height);
            }

        }

    }
}

/* ---------------------------------------------------------------------------
singleClip(....);

Parameters
    cableDiameter  --> the diameter of the cable to be clipped in [mm]
    cablePassage   --> size of the cable passage to clip the cable in [mm]
    thickness      --> thickness of the clips
    height         --> height of cable clip
--------------------------------------------------------------------------- */
module singleClip(cableDiameter, cablePassage, thickness, height) {
    cableCutOut = min(cablePassage,cableDiameter);
    innerRadius = cableDiameter / 2;
    outerRadius = innerRadius + thickness;
    nothing     = 0.01; // prevent z-fighting during preview
    difference() {
        union() {
            cylinder(height,outerRadius, outerRadius, false);
            translate([-outerRadius,0,0])
                cube([2*outerRadius,outerRadius,height], false);
        }
        translate([0,0,-nothing])
            cylinder(height+2*nothing,innerRadius, innerRadius, false);
        // cable passage
        translate([-(cableCutOut)/2,0,-nothing])
            cube([cableCutOut,outerRadius+nothing,height+2*nothing],false);

        // generate rounded corners
        Xpos1 = cableCutOut/2+1;
        Xpos2 = outerRadius-1;
        Ypos  = outerRadius-1;

        // rounded corner outer, left
        translate([-Xpos2,Ypos,0])
            clipFillet(-1,0,height);
        // rounded corner inner, left
        translate([-Xpos1,Ypos,0])
            clipFillet(0,0,height);
        // rounded corner inner, right
        translate([Xpos1,Ypos,0])
            clipFillet(-1,0,height);
        // rounded corner outer, right
        translate([Xpos2,Ypos,0])
            clipFillet(0,0,height);
    }
}

/* ---------------------------------------------------------------------------
clipFillet(....);

Parameters
    xdir   --> direction in X
    ydir   --> direction in Y
    height --> height of the fillet
Example
    right-upper =  0, 0
    right-lower =  0,-1
    left-lower  = -1,-1
    left-upper  = -1, 0
--------------------------------------------------------------------------- */
module clipFillet(xdir,ydir,height) {
    nothing = 0.01; // prevent z-fighting during preview
    difference() {
    translate([xdir-nothing,ydir-nothing,-nothing])
        cube([1+2*nothing,1+2*nothing,height+2*nothing],false);
    cylinder(height,1,1);
    }
}

/* ---------------------------------------------------------------------------
filler(....);

Parameters
    cableDiameter  --> the diameter of the cable to be clipped in [mm]
    thickness      --> thickness of the clips
    height         --> height of cable clip
--------------------------------------------------------------------------- */
module clipFiller(cablediameter,thickness,height) {
    nothing = 0.01;
    innerRadius = cableDiameter / 2;
    outerRadius = innerRadius + thickness-nothing;
    shift       = cableDiameter+ thickness;

    x1 = 0;
    y1 = 0;
    x2 = x1 + cos(0) * shift; // 1
    y2 = y1 + sin(0) * shift; // 0
    x3 = x1 + cos(60) * shift;
    y3 = y1 + sin(60) * shift;
    triangle = [[x1,y1],[x2,y2],[x3,-y3]];
    difference() {
        translate([0,0,nothing]) {
            linear_extrude(height =height-2*nothing, center = false, convexity = 0, twist = 0)
                polygon(triangle);
        }
        translate([x1,y1,0]) cylinder(height, outerRadius,outerRadius, false);

        translate([x2,y2,0]) cylinder(height, outerRadius,outerRadius, false);
        translate([x3,-y3,0]) cylinder(height, outerRadius,outerRadius, false);
    }
}

/* ---------------------------------------------------------------------------
 * EOF
--------------------------------------------------------------------------- */

```


[User]
Hey, I'm looking to create some customizable drawers for my desk organizer system, and I want to use OpenSCAD. I've been using the "Dave's Modular Organizer" system (DMO) which is based on a 1.5-inch grid.  I really like the look of the hex pattern on the side of this project, [DMO_-_drawer_box_v5_-_hex_pattern.scad](https://github.com/antonaccid/ai-scad-assistant/blob/main/DMO_-_drawer_box_v5_-_hex_pattern.scad), so I'd like something similar.

I need a few things:

1.  **Drawer Boxes:** I want to create drawer boxes (the housings) that can hold multiple drawers.  I should be able to specify how many drawers each box should hold (1, 2, 3, or 4) and I also need to adjust the height, depth, and width of the entire box. I need to be able to adjust wall thickness, floor thickness, and a corner radius for the boxes as well.
2.  **Drawers:** I need a set of drawers that will fit into these boxes. The drawers also need to have customizable height, depth, and width. There should be options for having one, two, three, or four sections/compartments within the drawer (using internal dividers) . I also want to be able to control the wall thickness and corner radius of the drawer itself, independent of the drawer box.
3.  **Hex Pattern:** I love that hex pattern on the sides, so I'd like to be able to turn it on/off and modify the parameters, like size of the hexagon, spacing between them, etc. for both drawer boxes and drawer sides.
4.  **Drawer Handle:** A basic drawer handle that I can adjust the size of.
5. **Clearances:** I need to be able to easily adjust the clearance between the drawers and the drawer box. I need to be able to adjust both horizontal and vertical clearances.

It would be great to have all of this in a parametric OpenSCAD file(s) -- using variables, comments, and sections to make it easily adjustable.  I'd like it to be similar in style to the file I linked to, so please include those parameters, too, like `$fn` and `FILLETS` (boolean).

I want to keep it fairly straightforward, and I don't need any libraries beyond the standard ones for now. I want it to be a complete, ready-to-print project, not just components, so make sure it renders in a way that I can see the whole thing!

Thanks so much, I'm excited to see what you come up with!

[Assistant]
DMO_-_drawer_box_v5_-_hex_pattern.scad:
```
/*

Customizable drawer box
Gian Pablo Villamil
August 4, 2014

Edited for DMO (2/20/2024)

v1 first iteration
v2 added drawer handle
v3 added full fillets, tweaked wall thickness and corner handling
v4 added text label
v5 added hex patterning on the sides

*/

//use<write.scad>;

/* [Global] */

// Which one would you like to see?
part = "drawerbox"; // [drawer1:1 section drawer,drawer2:2 section drawer,drawer3:3 section drawer, drawer4:4 section drawer, drawerbox:drawer housing]

/* [Basic] */

// How many drawers?
NUM_DRAWERS = 3;

// How high?
DRAWER_HEIGHT = 22.2;

// How deep?
DRAWER_DEPTH = 75.2;

// How wide?
DRAWER_WIDTH = 73.4;

// Label on the drawer
//MESSAGE = " ";

// Text height
//TEXT_HEIGHT = 6;

/* [Pattern] */

india = 6 ;
spacing = 1;
dia = india + (spacing * 2);

hoff = india+spacing;
voff = sqrt(pow(hoff,2)-pow(hoff/2,2));

/* [Advanced] */

// Handle size?
HANDLE_SIZE = 10;

// Corner radius?
DRAWER_CORNER_RAD = 2;

// Clearance
CLEARANCE = 0.2;

// Wall thickness
WALL_THICK = 1.6;

// Floor thickness
FLOOR_THICK = 1.6;

// Rounded fillets?
FILLETS = false;

/* [Hidden] */

$fn = 32;
NOFILLETS = !FILLETS;
WALL_WIDTH = WALL_THICK; //Hack to trick Makerware into making the right number of filament widths
INNER_FILLET = DRAWER_CORNER_RAD-(WALL_WIDTH/1.25);

BOX_HEIGHT = NUM_DRAWERS*(DRAWER_HEIGHT+CLEARANCE*2+WALL_WIDTH)+WALL_WIDTH;
BOX_WIDTH = DRAWER_WIDTH+CLEARANCE*2+WALL_WIDTH*2;
BOX_DEPTH = DRAWER_DEPTH+CLEARANCE+WALL_WIDTH;

thick = WALL_THICK + 0.2;

//
print_part();

module print_part() {
	if (part == "drawer1") {
		drawer1();
	} else if (part == "drawer2") {
		drawer2();
	} else if (part == "drawer3") {
		drawer3();
	} else if (part == "drawer4") {
		drawer4();
	} else {
		drawerbox();
	}
}

module drawerbox() {
	translate([-BOX_WIDTH/2,-BOX_DEPTH/2,0])
	difference () {
		cube([BOX_WIDTH,BOX_DEPTH,BOX_HEIGHT]);
		for (i=[0:NUM_DRAWERS-1]) {
			translate([WALL_WIDTH,-1,i*(DRAWER_HEIGHT+CLEARANCE*2+WALL_WIDTH)+WALL_WIDTH])
			cube([DRAWER_WIDTH+CLEARANCE*2,DRAWER_DEPTH+CLEARANCE+1,DRAWER_HEIGHT+CLEARANCE*2]);
		}
	translate([0,BOX_DEPTH+0.1,0])
	holes(BOX_WIDTH,BOX_HEIGHT);
	rotate([0,0,90]) translate([hoff/2,0.1,0]) holes(BOX_DEPTH,BOX_HEIGHT);
	rotate([0,0,90]) translate([hoff/2,-BOX_WIDTH+WALL_WIDTH+0.1,0]) holes(BOX_DEPTH,BOX_HEIGHT);
	}
};

module drawer1() {
	difference() {
		drawerbase();
		 translate([0,0,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH-(WALL_WIDTH*2),DRAWER_DEPTH-(WALL_WIDTH*2),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);
	}
};

module drawer2() {
	difference() {
		drawerbase();
		translate([-(DRAWER_WIDTH/4-WALL_WIDTH/4),0,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH-(WALL_WIDTH*2),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);
		translate([DRAWER_WIDTH/4-WALL_WIDTH/4,0,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH-(WALL_WIDTH*2),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);
}
};

module drawer3() {
	difference() {
		drawerbase();
		translate([-(DRAWER_WIDTH/4-WALL_WIDTH/4),0,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH-(WALL_WIDTH*2),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);

		translate([DRAWER_WIDTH/4-WALL_WIDTH/4,DRAWER_DEPTH/4-WALL_WIDTH/4,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH/2-(WALL_WIDTH*1.5),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);

		translate([DRAWER_WIDTH/4-WALL_WIDTH/4,-(DRAWER_DEPTH/4-WALL_WIDTH/4),FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH/2-(WALL_WIDTH*1.5),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);
}

};

module drawer4() {
	difference() {
		drawerbase();
		translate([-(DRAWER_WIDTH/4-WALL_WIDTH/4),DRAWER_DEPTH/4-WALL_WIDTH/4,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH/2-(WALL_WIDTH*1.5),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);

		translate([-(DRAWER_WIDTH/4-WALL_WIDTH/4),-(DRAWER_DEPTH/4-WALL_WIDTH/4),FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH/2-(WALL_WIDTH*1.5),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);

		translate([DRAWER_WIDTH/4-WALL_WIDTH/4,DRAWER_DEPTH/4-WALL_WIDTH/4,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH/2-(WALL_WIDTH*1.5),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);

		translate([DRAWER_WIDTH/4-WALL_WIDTH/4,-(DRAWER_DEPTH/4-WALL_WIDTH/4),FLOOR_THICK+DRAWER_CORNER_RAD/2])
			roundedBox([DRAWER_WIDTH/2-(WALL_WIDTH*1.5),DRAWER_DEPTH/2-(WALL_WIDTH*1.5),DRAWER_HEIGHT+DRAWER_CORNER_RAD],INNER_FILLET,NOFILLETS);
}};

module drawer6() {};

module drawerbase() {
	union() {
	difference() {
	translate([0,0,DRAWER_CORNER_RAD/2]) roundedBox([DRAWER_WIDTH,DRAWER_DEPTH,DRAWER_HEIGHT+DRAWER_CORNER_RAD],DRAWER_CORNER_RAD,NOFILLETS);
	translate([0,0,DRAWER_HEIGHT]) cube([DRAWER_WIDTH+1, DRAWER_DEPTH+1,DRAWER_HEIGHT],center=true);}
translate([0,-DRAWER_DEPTH/2,-DRAWER_HEIGHT/2+FLOOR_THICK]) handle();

//translate([-DRAWER_WIDTH/2+DRAWER_CORNER_RAD,-DRAWER_DEPTH/2+0.5,-DRAWER_HEIGHT/4+DRAWER_CORNER_RAD/2]) rotate([90,0,0]) write(MESSAGE,h=TEXT_HEIGHT,font="Orbitron.dxf");
};
};

module handle() {

	difference() {
		roundedBox([DRAWER_WIDTH/4,HANDLE_SIZE*2,FLOOR_THICK*2],2,true);
		translate([0,HANDLE_SIZE,0]) cube([DRAWER_WIDTH/4+1,HANDLE_SIZE*2,FLOOR_THICK*2+1],center=true);
	};

	difference() {
		translate([0,-DRAWER_CORNER_RAD/2,FLOOR_THICK+DRAWER_CORNER_RAD/2])
			cube([DRAWER_WIDTH/4,DRAWER_CORNER_RAD,DRAWER_CORNER_RAD],center=true);
		translate([-DRAWER_WIDTH/8-1,-DRAWER_CORNER_RAD,FLOOR_THICK+DRAWER_CORNER_RAD]) rotate([0,90,0])
			cylinder(h=DRAWER_WIDTH/4+2,r=DRAWER_CORNER_RAD);
		};

	difference() {
		translate([-DRAWER_WIDTH/8,0,-FLOOR_THICK])
			cube([DRAWER_WIDTH/4,DRAWER_CORNER_RAD,DRAWER_CORNER_RAD],center=false);
		translate([-DRAWER_WIDTH/8-1,DRAWER_CORNER_RAD,-FLOOR_THICK+DRAWER_CORNER_RAD]) rotate([0,90,0])
			cylinder(h=DRAWER_WIDTH/4+2,r=DRAWER_CORNER_RAD);
		};
};

module holes(width,height) {
	cols = width / hoff - DRAWER_CORNER_RAD;
	rows = height / voff - DRAWER_CORNER_RAD;

	translate([hoff*1.3,0,voff*2])
	for (i=[0:rows-1]) {
		for (j=[0:cols-1]){
			translate([j*hoff+i%2*(hoff/2),0,i*voff])
			rotate([90,90,0]) rotate([0,0,0]) cylinder(h=thick,r=india/2,$fn=6);
		}
	}
}

// translate([DRAWER_WIDTH/2+WALL_WIDTH+CLEARANCE,DRAWER_DEPTH/2,DRAWER_HEIGHT/2+FLOOR_THICK+CLEARANCE])
// drawerbase();
// handle();
// drawer1();
// drawer2();
// drawer3();
// drawer4();
// translate([-BOX_WIDTH/2,-BOX_DEPTH-thick,BOX_HEIGHT/2])
// drawerbox();
// holes(BOX_WIDTH,BOX_HEIGHT);
%cube([200,200,1],center=true);

// Library: boxes.scad
// Version: 1.0
// Author: Marius Kintel
// Copyright: 2010
// License: BSD

// roundedBox([width, height, depth], float radius, bool sidesonly);

// EXAMPLE USAGE:
// roundedBox([20, 30, 40], 5, true);

// size is a vector [w, h, d]
module roundedBox(size, radius, sidesonly)
{
  rot = [ [0,0,0], [90,0,90], [90,90,0] ];
  if (sidesonly) {
    cube(size - [2*radius,0,0], true);
    cube(size - [0,2*radius,0], true);
    for (x = [radius-size[0]/2, -radius+size[0]/2],
           y = [radius-size[1]/2, -radius+size[1]/2]) {
      translate([x,y,0]) cylinder(r=radius, h=size[2], center=true);
    }
  }
  else {
    cube([size[0], size[1]-radius*2, size[2]-radius*2], center=true);
    cube([size[0]-radius*2, size[1], size[2]-radius*2], center=true);
    cube([size[0]-radius*2, size[1]-radius*2, size[2]], center=true);

    for (axis = [0:2]) {
      for (x = [radius-size[axis]/2, -radius+size[axis]/2],
             y = [radius-size[(axis+1)%3]/2, -radius+size[(axis+1)%3]/2]) {
        rotate(rot[axis])
          translate([x,y,0])
          cylinder(h=size[(axis+2)%3]-2*radius, r=radius, center=true);
      }
    }
    for (x = [radius-size[0]/2, -radius+size[0]/2],
           y = [radius-size[1]/2, -radius+size[1]/2],
           z = [radius-size[2]/2, -radius+size[2]/2]) {
      translate([x,y,z]) sphere(radius);
    }
  }
}

```