// ER11 collet tray for an openGrid board in a drawer.
// Collets stored taper-down; labels are laser engraved (er11_engrave.svg). All dims in mm.

// ---- collet pockets ----
seat_dia    = 10.9;  // pocket dia at top surface (ER11 OD 11.5 -> collet sits ~2 mm proud). Tune with er11_fit_test.stl
half_angle  = 8;     // ER taper half-angle
cone_depth  = 14;
relief_dia  = 6.5;   // through-hole so collets never bottom out; chips fall through
height      = 16;    // tray body height above the board
cols = 5; rows = 3;
pitch_x = 22; pitch_y = 24;
pocket_off_y = 4;    // pockets toward the back of each cell, label strip in front
chamfer = 0.6;
corner_r = 4;

// ---- collet nut row (front). Nut sits thread-side down over a centring post ----
nut_count    = 4;
nut_recess_d = 20;   // takes 16 mm round and 17 mm hex ER11 nuts
nut_recess_h = 5;
nut_post_d   = 11;   // fits inside the M14x0.75 thread bore
nut_post_h   = 4;
nut_center_y = 20;   // from front edge

// ---- openGrid ----
grid = 28;
gx = 4; gy = 4;          // footprint in openGrid cells
foot_clear = 0.5;        // total clearance so neighbouring items don't bind
plug_flats  = 24.8;      // cell land is 25.0 across flats
plug_corner = 14.129;    // centre-to-corner-facet (cell 14.229)
plug_depth  = 3.4;       // works in openGrid Full (6.8) and Lite (4.0)
plug_chamfer = 0.6;
plug_clear  = 0;         // shrinks the plugs; 0.05-0.1 if they are too tight in the board

W = gx*grid - foot_clear;
D = gy*grid - foot_clear;
edge_x = (W - cols*pitch_x)/2;
edge_y = 5.75;   // back margin; the nut row fills the front
top = height + plug_depth;
$fn = 96;

module octagon(f, c) intersection() { square(f, center=true); rotate(45) square(2*c, center=true); }

module plug() {
    f = plug_flats - 2*plug_clear; c = plug_corner - plug_clear; ch = plug_chamfer;
    hull() {
        linear_extrude(0.01) octagon(f - 2*ch, c - ch);
        translate([0,0,ch]) linear_extrude(0.01) octagon(f, c);
    }
    translate([0,0,ch]) linear_extrude(plug_depth - ch + 0.01) octagon(f, c);
}

module pocket() {
    bot = seat_dia - 2*cone_depth*tan(half_angle);
    translate([0,0,-cone_depth]) cylinder(h=cone_depth+0.01, d1=bot, d2=seat_dia);
    translate([0,0,-chamfer]) cylinder(h=chamfer+0.01, d1=seat_dia, d2=seat_dia+2*chamfer+0.02);
    translate([0,0,-top-1]) cylinder(h=top+2, d=relief_dia);
    cylinder(h=1, d=seat_dia+2*chamfer);
}

difference() {
    union() {
        translate([0,0,plug_depth]) linear_extrude(height)
            offset(r=corner_r) offset(delta=-corner_r) square([W, D]);
        for (i=[0:gx-1], j=[0:gy-1])
            translate([grid*(i+0.5) - foot_clear/2, grid*(j+0.5) - foot_clear/2, 0]) plug();
    }
    for (r=[0:rows-1], c=[0:cols-1])
        translate([edge_x + pitch_x*(c+0.5), D - edge_y - pitch_y*(r+0.5) + pocket_off_y, top]) pocket();
    for (i=[0:nut_count-1]) translate([W/nut_count*(i+0.5), nut_center_y, top - nut_recess_h]) {
        cylinder(h=nut_recess_h+1, d=nut_recess_d);
        translate([0,0,nut_recess_h-chamfer]) cylinder(h=chamfer+0.01, d1=nut_recess_d, d2=nut_recess_d+2*chamfer+0.02);
    }
}
for (i=[0:nut_count-1]) translate([W/nut_count*(i+0.5), nut_center_y, top - nut_recess_h])
    cylinder(h=nut_post_h, d1=nut_post_d, d2=nut_post_d-1);
