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
+103 -77
+103 -77
View File
@@ -8,12 +8,12 @@
cases = 4; cases = 4;
// Inner size of each compartment: X * X * X (mm) // Inner size of each compartment: X * X * X (mm)
X = 20; X = 20;
// Text engraved on the lid // Text engraved on the lid (runs parallel to the sliding direction)
engraving = "Monday"; engraving = "Monday";
/* [Engraving] */ /* [Engraving] */
text_size = 6; text_size = 6;
text_depth = 0.6; text_depth = 0.6; // engraved with sloped walls (see dt_ang)
text_font = "Overpass:style=Bold"; text_font = "Overpass:style=Bold";
/* [Structure] */ /* [Structure] */
@@ -21,33 +21,39 @@ wall = 3.2; // outer side walls and front wall
divider = 1.6; // wall between compartments divider = 1.6; // wall between compartments
floor_t = 1.6; // floor thickness floor_t = 1.6; // floor thickness
lid_h = 3.0; // lid 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) end_wall = 5.5; // rear wall thickness (holds the clip groove)
corner_r = 1.5; // vertical corner rounding corner_r = 1.5; // vertical corner rounding
/* [Tolerances] */ /* [Dovetail] */
// Sliding clearance between lid and box // 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; tol = 0.25;
// Clearance inside the snap pockets (smaller = tighter, less rattle) // Clearance inside the snap pockets (smaller = tighter, less rattle)
snap_tol = 0.15; snap_tol = 0.15;
/* [Snap] */ /* [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) // Rear bump: how far it is pushed aside while sliding (click strength)
detent = 0.7; detent = 0.5;
// Side bumps: same, but sideways // Side ridges: same, but sideways
side_detent = 0.6; side_detent = 0.5;
// Side bump length along the lid face // Flexible tab (rear bump): width, thickness, slot width
side_len = 1.8; clip_w = 5;
// Flexible tab (rear bump): width, max length, slot width tab_t = 1.2; // thinner = softer (the underside of the tab is relieved)
clip_w = 4.5;
clip_len_max = 28;
clip_slot = 1.0; clip_slot = 1.0;
// Flexible side arms: thickness (stiffness) and slot width // Flexible side arms: thickness (stiffness) and slot width, both measured
arm_t = 1.6; // perpendicular to the sloped faces
arm_t = 1.2;
arm_slot = 1.2; arm_slot = 1.2;
/* [Display] */ /* [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] part = "plate"; // [plate, box, lid, assembly]
// Assembly preview only: push the lid open by this many mm // Assembly preview only: push the lid open by this many mm
lid_offset = 0; lid_offset = 0;
@@ -58,6 +64,9 @@ lid_offset = 0;
$fn = 48; $fn = 48;
eps = 0.01; eps = 0.01;
m = tan(dt_ang); // sideways shift per mm of height
cs = cos(dt_ang);
W = X + 2*wall; // outer width (Y) W = X + 2*wall; // outer width (Y)
x_cav_end = wall + cases*X + (cases-1)*divider; // end of last compartment x_cav_end = wall + cases*X + (cases-1)*divider; // end of last compartment
L = x_cav_end + end_wall; // outer length (X) 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 hg = lid_h + tol; // height of the lid groove
H = z0 + hg; // total height H = z0 + hg; // total height
gb = X/2 + rail_ledge; // groove half width at z0 gb = X/2 + rail_ledge; // groove half width at z0
k45 = tol*sqrt(2); // horizontal offset = tol on a 45 deg face k = tol/cs; // horizontal gap = tol on the sloped faces
lw_b = gb - tol - k45; // lid half width at its bottom lw_b = gb - m*tol - k; // lid half width at its bottom
lw_t = gb - hg - k45; // lid half width at its top 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_x0 = wall + tol; // lid front (closed position)
lid_len = L - lid_x0; // lid rear is flush with box rear 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 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 cham = detent + tol; // lead-in chamfer on rear wall
xb = x_cav_end + (end_wall - cham)/2; // x of all detents xb = x_cav_end + (end_wall - cham)/2; // x of all detents
zm = tol + lid_h/2; // mid height of the lid faces
// 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;
assert(cases >= 1, "cases must be >= 1"); assert(cases >= 1, "cases must be >= 1");
assert(rail_ledge < wall, "rail_ledge must be smaller than wall"); assert(dt_ang >= 15 && dt_ang <= 45, "dt_ang must be between 15 and 45");
assert(zs_lo <= zs_hi, "Lid too thin for the side bumps: increase lid_h or reduce side_len / side_detent"); 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(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), assert(lw_t - d2 - (clip_w/2 + clip_slot) >= 1.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"); "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")); 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 // Helpers
@@ -102,7 +119,7 @@ echo(str("Outer size: ", L, " x ", W, " x ", H, " mm"));
module yz_extrude(len) { module yz_extrude(len) {
rotate([90, 0, 90]) linear_extrude(height = len) children(); 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) { module xz_extrude(w) {
rotate([90, 0, 0]) linear_extrude(height = w, center = true) children(); 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]); translate([0, -w/2]) offset(r = r) offset(delta = -r) square([l, w]);
} }
// Side bump // Side ridge profile: x = sliding direction, y = face normal.
module side_bump_3d(extra = 0) { // It runs along the whole sloped face (no end faces -> no overhang).
h = sb_h + extra; module side_bump_2d() {
len = side_len + extra*2; polygon([[-sb_h, -0.3], [sb_h, -0.3], [sb_h, 0], [0, sb_h], [-sb_h, 0]]);
// 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);
} }
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 module on_side(s) { // s = +1 right side, -1 left side
translate([xb, s*(lw_b - (zs - tol)), z0 + zs]) translate([xb, s*(gb - m*zm - k), z0 + zm])
if (s > 0) rotate([45, 0, 0]) children(); if (s > 0) rotate([dt_ang, 0, 0]) children();
else mirror([0, 1, 0]) rotate([45, 0, 0]) children(); else mirror([0, 1, 0]) rotate([dt_ang, 0, 0]) children();
} }
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
@@ -149,8 +159,8 @@ module box() {
translate([wall, 0, z0]) translate([wall, 0, z0])
yz_extrude(L - wall + eps) yz_extrude(L - wall + eps)
polygon([[-gb, 0], [gb, 0], polygon([[-gb, 0], [gb, 0],
[ gb - hg - eps, hg + eps], [ gb - m*(hg + eps), hg + eps],
[-(gb - hg - eps), hg + eps]]); [-(gb - m*(hg + eps)), hg + eps]]);
// V groove in the rear wall for the tab bump // V groove in the rear wall for the tab bump
translate([xb, 0, 0]) translate([xb, 0, 0])
@@ -160,9 +170,11 @@ module box() {
[ bump_h, z0 + tol], [ bump_h, z0 + tol],
[ 0, z0 + tol - bump_h]]); [ 0, z0 + tol - bump_h]]);
// dimples in the rails for the side bumps // V channels along the rail faces for the side ridges
for (s = [-1, 1]) on_side(s) intersection() {
side_bump_3d(snap_tol); 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 // lead-in chamfer so the tab bump can climb onto the rear wall
xz_extrude(X) 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) { module lid(bumps = true) {
text_x = (lid_x0 + L - clip_len)/2; text_x = (lid_x0 + L - clip_len)/2;
d1 = arm_t*sqrt(2);
d2 = (arm_t + arm_slot)*sqrt(2);
za = tol - 1; za = tol - 1;
zb = hg + 1; zb = hg + 1;
// x-offset of the side face at height z (right side) // y of the right sloped face, moved inwards by d (horizontal), at height z
function yf(z, d) = lw_b - (z - tol) - d; function yf(z, d) = lw_b - m*(z - tol) - d;
difference() { difference() {
union() { union() {
// lid plate (dovetail profile) // lid plate (dovetail profile), underside of the tab relieved
difference() {
translate([lid_x0, 0, z0]) translate([lid_x0, 0, z0])
yz_extrude(lid_len) yz_extrude(lid_len)
polygon([[-lw_b, tol], [lw_b, tol], polygon([[-lw_b, tol], [lw_b, tol],
[ lw_t, hg], [-lw_t, hg]]); [ 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) { 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]) translate([xb, 0, 0])
xz_extrude(clip_w) xz_extrude(clip_w)
polygon([[-bump_h, z0 + tol + eps], polygon([[-(bump_h + tab_r), z0 + tol + tab_r + eps],
[ bump_h, z0 + tol + eps], [ bump_h + tab_r, z0 + tol + tab_r + eps],
[ 0, z0 + tol - bump_h]]); [ 0, z0 + tol - bump_h]]);
// side ridges, cut flush with the lid top and bottom planes
// side bumps on the flexible arms intersection() {
for (s = [-1, 1]) on_side(s) union() for (s = [-1, 1]) on_side(s) side_prism(0);
side_bump_3d(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]) for (s = [-1, 1])
translate([L - clip_len, translate([L - clip_len,
s*(clip_w/2 + clip_slot/2) - clip_slot/2, s*(clip_w/2 + clip_slot/2) - clip_slot/2,
z0 - 1]) z0 - 1])
cube([clip_len + 1, clip_slot, hg + 2]); 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]) for (s = [-1, 1])
translate([L - clip_len, 0, z0]) translate([L - clip_len, 0, z0])
yz_extrude(clip_len + 1) yz_extrude(clip_len + 1)
@@ -223,10 +252,7 @@ module lid(bumps = true) {
[yf(zb, d2), zb], [yf(za, d2), za]]); [yf(zb, d2), zb], [yf(za, d2), za]]);
// engraving, parallel to the sliding direction // engraving, parallel to the sliding direction
translate([text_x, 0, z0 + hg - text_depth]) engraving_cut(text_x);
linear_extrude(text_depth + eps)
text(engraving, size = text_size, font = text_font,
halign = "center", valign = "center");
} }
} }