improve clip in strength

This commit is contained in:
sam.hadow committed 2026-10-08 11:46:00 +02:00
1 parent 773a4d40ff
commit 6737bd36a0
1 file changed
+109 -83
+109 -83
View File
@@ -8,12 +8,12 @@
cases = 4;
// Inner size of each compartment: X * X * X (mm)
X = 20;
// Text engraved on the lid
// Text engraved on the lid (runs parallel to the sliding direction)
engraving = "Monday";
/* [Engraving] */
text_size = 6;
text_depth = 0.6;
text_depth = 0.6; // engraved with sloped walls (see dt_ang)
text_font = "Overpass:style=Bold";
/* [Structure] */
@@ -21,33 +21,39 @@ wall = 3.2; // outer side walls and front wall
divider = 1.6; // wall between compartments
floor_t = 1.6; // floor thickness
lid_h = 3.0; // lid thickness
rail_ledge = 2.4; // width of the ledge the lid edges rest on (< wall)
rail_ledge = 1.4; // width of the ledge the lid edges rest on (< wall)
end_wall = 5.5; // rear wall thickness (holds the clip groove)
corner_r = 1.5; // vertical corner rounding
/* [Tolerances] */
// Sliding clearance between lid and box
/* [Dovetail] */
// Angle of the sliding faces from vertical = worst overhang of the print.
// 30 prints cleanly without supports; lower = even safer but less grip.
dt_ang = 30; // [15:1:45]
/* [Fit] */
// Sliding clearance between lid and box (PETG: 0.2 - 0.3)
tol = 0.25;
// Clearance inside the snap pockets (smaller = tighter, less rattle)
snap_tol = 0.15;
/* [Snap] */
// Length of the slots in the lid = length of the flexible tab and arms.
clip_len = 11.4;
// Rear bump: how far it is pushed aside while sliding (click strength)
detent = 0.7;
// Side bumps: same, but sideways
side_detent = 0.6;
// Side bump length along the lid face
side_len = 1.8;
// Flexible tab (rear bump): width, max length, slot width
clip_w = 4.5;
clip_len_max = 28;
detent = 0.5;
// Side ridges: same, but sideways
side_detent = 0.5;
// Flexible tab (rear bump): width, thickness, slot width
clip_w = 5;
tab_t = 1.2; // thinner = softer (the underside of the tab is relieved)
clip_slot = 1.0;
// Flexible side arms: thickness (stiffness) and slot width
arm_t = 1.6;
// Flexible side arms: thickness (stiffness) and slot width, both measured
// perpendicular to the sloped faces
arm_t = 1.2;
arm_slot = 1.2;
/* [Display] */
// plate = both parts laid out for printing, assembly = closed for preview
// plate = both parts laid out for printing, assembly = closed, for preview
part = "plate"; // [plate, box, lid, assembly]
// Assembly preview only: push the lid open by this many mm
lid_offset = 0;
@@ -58,6 +64,9 @@ lid_offset = 0;
$fn = 48;
eps = 0.01;
m = tan(dt_ang); // sideways shift per mm of height
cs = cos(dt_ang);
W = X + 2*wall; // outer width (Y)
x_cav_end = wall + cases*X + (cases-1)*divider; // end of last compartment
L = x_cav_end + end_wall; // outer length (X)
@@ -65,35 +74,43 @@ z0 = floor_t + X; // top of compartments
hg = lid_h + tol; // height of the lid groove
H = z0 + hg; // total height
gb = X/2 + rail_ledge; // groove half width at z0
k45 = tol*sqrt(2); // horizontal offset = tol on a 45 deg face
lw_b = gb - tol - k45; // lid half width at its bottom
lw_t = gb - hg - k45; // lid half width at its top
k = tol/cs; // horizontal gap = tol on the sloped faces
lw_b = gb - m*tol - k; // lid half width at its bottom
lw_t = gb - m*hg - k; // lid half width at its top
d1 = arm_t/cs; // horizontal offsets of the arm slot
d2 = (arm_t + arm_slot)/cs; // walls from the sloped face
lid_x0 = wall + tol; // lid front (closed position)
lid_len = L - lid_x0; // lid rear is flush with box rear
clip_len = min(clip_len_max, lid_len*0.45); // tab and arm length
tab_r = lid_h - tab_t; // relief under the tab (softens it)
// Engraving: shrink the text if it would not fit between the lid front
// and the slots (average bold glyph is ~0.9 * text_size wide, conservative)
text_zone = lid_len - clip_len - 2*2; // 2 mm margin at both ends
text_fit = min(text_size, text_zone/(len(engraving)*0.9));
bump_h = tol + detent; // rear V bump height
sb_h = tol + side_detent; // side bump height
sb_h = tol + side_detent; // side ridge height
cham = detent + tol; // lead-in chamfer on rear wall
xb = x_cav_end + (end_wall - cham)/2; // x of all detents
// Height (above z0) of the side bump centre on the sloped lid face, chosen so that bump and dimple stay clear of the groove bottom and of the rail top
sb_hl = side_len/2 + snap_tol;
sb_up = sqrt(0.5)*(sb_hl + sb_h + snap_tol);
sb_dn = sqrt(0.5)*(sb_hl + 0.2 + snap_tol);
zs_lo = 0.15 + sb_dn;
zs_hi = hg - 0.35 - sb_up;
zs = (zs_lo + zs_hi)/2;
zm = tol + lid_h/2; // mid height of the lid faces
assert(cases >= 1, "cases must be >= 1");
assert(rail_ledge < wall, "rail_ledge must be smaller than wall");
assert(zs_lo <= zs_hi, "Lid too thin for the side bumps: increase lid_h or reduce side_len / side_detent");
assert(dt_ang >= 15 && dt_ang <= 45, "dt_ang must be between 15 and 45");
assert(rail_ledge < wall - 1, "rail_ledge must be at least 1 mm smaller than wall");
assert(m*lid_h - k >= 1, "Dovetail grip too small: increase lid_h or dt_ang");
assert(arm_slot > side_detent + 0.2, "arm_slot must exceed side_detent + 0.2");
assert(clip_w/2 + clip_slot + 1.2 < lw_t - (arm_t + arm_slot)*sqrt(2),
"Tab, slots and side arms do not fit in the lid (X is too small, about 20 mm minimum with default settings): decrease clip_w / clip_slot / arm_t / arm_slot or increase X");
assert(lw_t - d2 - (clip_w/2 + clip_slot) >= 1.2,
"Tab, slots and side arms do not fit in the lid (X too small, about 18 mm minimum with default settings): reduce clip_w / clip_slot / arm_t / arm_slot or increase X");
assert(tab_t >= 0.8 && tab_t <= lid_h, "tab_t must be between 0.8 and lid_h");
assert(lid_len >= clip_len + 8, "Lid too short for this clip_len: reduce clip_len");
assert(text_zone > 4, "Lid too short for any engraving: reduce clip_len");
echo(str("Outer size: ", L, " x ", W, " x ", H, " mm"));
if (text_fit < text_size)
echo(str("Engraving scaled down from size ", text_size, " to ", text_fit));
if (text_fit < 3)
echo("WARNING: engraving is very small; use fewer characters, more cases or a shorter clip_len");
// ---------------------------------------------------------------------
// Helpers
@@ -102,7 +119,7 @@ echo(str("Outer size: ", L, " x ", W, " x ", H, " mm"));
module yz_extrude(len) {
rotate([90, 0, 90]) linear_extrude(height = len) children();
}
// Extrude a 2D (x,z) profile along Y, centered on y = 0
// Extrude a 2D (x,z) profile along Y, centred on y = 0
module xz_extrude(w) {
rotate([90, 0, 0]) linear_extrude(height = w, center = true) children();
}
@@ -110,27 +127,20 @@ module rrect(l, w, r) {
translate([0, -w/2]) offset(r = r) offset(delta = -r) square([l, w]);
}
// Side bump
module side_bump_3d(extra = 0) {
h = sb_h + extra;
len = side_len + extra*2;
// Pyramid pointing outwards in local +Y
rotate([-90, 0, 0])
scale([1, len/(2*h), 1])
cylinder(h = h, r1 = h, r2 = 0, $fn = 4);
// Embedded base
translate([0, -0.2, 0])
rotate([-90, 0, 0])
scale([1, len/(2*h), 1])
cylinder(h = 0.2, r1 = h, r2 = h, $fn = 4);
// Side ridge profile: x = sliding direction, y = face normal.
// It runs along the whole sloped face (no end faces -> no overhang).
module side_bump_2d() {
polygon([[-sb_h, -0.3], [sb_h, -0.3], [sb_h, 0], [0, sb_h], [-sb_h, 0]]);
}
module side_prism(grow = 0) {
linear_extrude(height = hg/cs + 4, center = true)
offset(delta = grow) side_bump_2d();
}
// Place children on the middle of the lid's sloped face
module on_side(s) { // s = +1 right side, -1 left side
translate([xb, s*(lw_b - (zs - tol)), z0 + zs])
if (s > 0) rotate([45, 0, 0]) children();
else mirror([0, 1, 0]) rotate([45, 0, 0]) children();
translate([xb, s*(gb - m*zm - k), z0 + zm])
if (s > 0) rotate([dt_ang, 0, 0]) children();
else mirror([0, 1, 0]) rotate([dt_ang, 0, 0]) children();
}
// ---------------------------------------------------------------------
@@ -149,8 +159,8 @@ module box() {
translate([wall, 0, z0])
yz_extrude(L - wall + eps)
polygon([[-gb, 0], [gb, 0],
[ gb - hg - eps, hg + eps],
[-(gb - hg - eps), hg + eps]]);
[ gb - m*(hg + eps), hg + eps],
[-(gb - m*(hg + eps)), hg + eps]]);
// V groove in the rear wall for the tab bump
translate([xb, 0, 0])
@@ -160,9 +170,11 @@ module box() {
[ bump_h, z0 + tol],
[ 0, z0 + tol - bump_h]]);
// dimples in the rails for the side bumps
for (s = [-1, 1]) on_side(s)
side_bump_3d(snap_tol);
// V channels along the rail faces for the side ridges
intersection() {
union() for (s = [-1, 1]) on_side(s) side_prism(snap_tol);
translate([xb - 5, -W, z0]) cube([10, 2*W, hg + 1]);
}
// lead-in chamfer so the tab bump can climb onto the rear wall
xz_extrude(X)
@@ -173,48 +185,65 @@ module box() {
}
}
// Engraving
engr_slices = 6;
module engraving_cut(tx) {
dz = text_depth/engr_slices;
for (i = [0 : engr_slices - 1])
translate([tx, 0, z0 + hg - (i + 1)*dz])
linear_extrude(dz + eps)
offset(delta = -i*dz*tan(dt_ang))
text(engraving, size = text_fit, font = text_font,
halign = "center", valign = "center");
}
// ---------------------------------------------------------------------
// Lid
// Lid (modelled in its closed position, engraved face up)
// ---------------------------------------------------------------------
module lid(bumps = true) {
text_x = (lid_x0 + L - clip_len)/2;
d1 = arm_t*sqrt(2);
d2 = (arm_t + arm_slot)*sqrt(2);
za = tol - 1;
zb = hg + 1;
// x-offset of the side face at height z (right side)
function yf(z, d) = lw_b - (z - tol) - d;
// y of the right sloped face, moved inwards by d (horizontal), at height z
function yf(z, d) = lw_b - m*(z - tol) - d;
difference() {
union() {
// lid plate (dovetail profile)
translate([lid_x0, 0, z0])
yz_extrude(lid_len)
polygon([[-lw_b, tol], [lw_b, tol],
[ lw_t, hg], [-lw_t, hg]]);
// lid plate (dovetail profile), underside of the tab relieved
difference() {
translate([lid_x0, 0, z0])
yz_extrude(lid_len)
polygon([[-lw_b, tol], [lw_b, tol],
[ lw_t, hg], [-lw_t, hg]]);
translate([L - clip_len, -(clip_w/2 + clip_slot), z0 + tol - 1])
cube([clip_len + 1, clip_w + 2*clip_slot, 1 + tab_r]);
}
if (bumps) {
// V bump under the free end of the tab
// V bump under the free end of the tab (45 deg flanks, grows
// from the relieved underside, apex at the same height as before)
translate([xb, 0, 0])
xz_extrude(clip_w)
polygon([[-bump_h, z0 + tol + eps],
[ bump_h, z0 + tol + eps],
[ 0, z0 + tol - bump_h]]);
// side bumps on the flexible arms
for (s = [-1, 1]) on_side(s)
side_bump_3d(0);
polygon([[-(bump_h + tab_r), z0 + tol + tab_r + eps],
[ bump_h + tab_r, z0 + tol + tab_r + eps],
[ 0, z0 + tol - bump_h]]);
// side ridges, cut flush with the lid top and bottom planes
intersection() {
union() for (s = [-1, 1]) on_side(s) side_prism(0);
translate([xb - 5, -W/2, z0 + tol]) cube([10, W, lid_h]);
}
}
}
// slots that free the central tab
// slots that free the central tab (vertical walls)
for (s = [-1, 1])
translate([L - clip_len,
s*(clip_w/2 + clip_slot/2) - clip_slot/2,
z0 - 1])
cube([clip_len + 1, clip_slot, hg + 2]);
// slots (parallel to the sloped faces) that free the side arms
// slots that free the side arms, parallel to the sloped faces so the
// arms keep a constant thickness (same angle as the dovetail)
for (s = [-1, 1])
translate([L - clip_len, 0, z0])
yz_extrude(clip_len + 1)
@@ -223,10 +252,7 @@ module lid(bumps = true) {
[yf(zb, d2), zb], [yf(za, d2), za]]);
// engraving, parallel to the sliding direction
translate([text_x, 0, z0 + hg - text_depth])
linear_extrude(text_depth + eps)
text(engraving, size = text_size, font = text_font,
halign = "center", valign = "center");
engraving_cut(text_x);
}
}
@@ -248,4 +274,4 @@ if (part == "plate") {
} else if (part == "assembly") {
box();
translate([lid_offset, 0, 0]) lid();
}
}