7-Day Pill Organizer
A row of seven compartments, one per day, each with its own lid so a single day can be opened without disturbing the rest. The lids are hinged in place and printed with the box: every lid knuckle carries 45 degree cones on both ends that sit in conical sockets of the neighbouring box knuckles, so the hinge is captive and needs no pin, and a friction lip under each lid holds it shut. The day names are engraved on the lids. The default has 20 x 22 x 14 mm compartments and prints as one plate of 153 x 56 x 16 mm with the lids open flat behind the box; every knuckle is a leaf with a round top, so no supports. After printing, bend each lid up firmly once to free its hinge. Adjustable parameters: compartment width, depth and height, wall thickness, hinge clearance and lip clearance.
# 7-Day Pill Organizer - seven compartments, each with its own print-in-place hinged lid
#
# A row of compartments, one per day, with a separate lid on every one so a single day
# can be opened. The lids are hinged in place: each lid knuckle carries 45 deg cones on
# both ends that sit in conical sockets of the neighbouring box knuckles, so the hinge is
# captive and needs no pin. A friction lip under each lid holds it shut. Day names are
# engraved on the lids.
# Print layout (as modelled): the box stands on its base, the lids lie open 180 deg
# behind it, upside down. Every knuckle is a leaf with a round top, so no supports.
# After printing, bend each lid up firmly once to free the hinge.
@param 14..30 step:1 group:"Size" desc:"Inner width of a compartment, along the row (mm)"
$cw = 20
@param 14..30 step:1 group:"Size" desc:"Inner depth of a compartment (mm)"
$cd = 22
@param 8..25 step:1 group:"Size" desc:"Inner height (mm)"
$ch = 14
@param 1.2..2.4 step:0.2 group:"Size" desc:"Wall thickness (mm)"
$wall = 1.6
@param 0.3..0.6 step:0.05 group:"Hinge" desc:"Hinge clearance, radial and axial (mm)"
$gap = 0.4
@param 0.1..0.3 step:0.05 group:"Lid" desc:"Friction lip clearance per side (mm)"
$lip_gap = 0.15
$days = ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"]
$n = 7
$D = $cd + 2 * $wall # outer depth
$H = $ch + $wall # box height
$lid_t = 2
$hinge_z = ($H + $lid_t) / 2 # a 180 deg swing puts the lid on the rim
$R = 3 # knuckle radius
$rc = 2 # cone pin base radius
$e = 0.5 # cone base sits this far inside its knuckle
$pitch = $cw + $wall
$L = $n * $pitch + $wall
assert $hinge_z >= $R + 0.5
# --- Box: one shell with n cavities; its back face sits R behind the hinge axis (y = 0)
$yb = -$R - $gap # the lid tombs (y down to -R) clear this face by gap
$shell = box $L $D $H center:(false, false, false) | translate 0 ($yb - $D) 0
$cavities = union [box $cw $cd ($ch + 1) center:(false, false, false) | translate ($wall + $i * $pitch) ($yb - $D + $wall) $wall for $i in range($n)]
# --- Hinge pieces (all along X at y = 0, z = hinge_z) -------------------------------
def slab($x0, $x1, $y0, $y1, $z0, $z1) = box ($x1 - $x0) ($y1 - $y0) ($z1 - $z0) center:(false, false, false) | translate $x0 $y0 $z0
def xcyl($r, $x0, $x1) = cylinder $r ($x1 - $x0) | rotate 0 90 0 | translate (($x0 + $x1) / 2) 0 $hinge_z
def tomb($x0, $x1) = slab($x0, $x1, -$R, $R, 0, $hinge_z) + xcyl($R, $x0, $x1)
def cone_pos($r, $xb) = cone $r 0 $r | rotate 0 90 0 | translate ($xb + $r / 2) 0 $hinge_z
def cone_neg($r, $xb) = cone 0 $r $r | rotate 0 90 0 | translate ($xb - $r / 2) 0 $hinge_z
# compartment i: lid span [la, lb]; knuckles box / lid / box of length seg
def la($i) = $i * $pitch + $wall / 2 + 0.4
def lb($i) = ($i + 1) * $pitch + $wall / 2 - 0.4
def seg($i) = (lb($i) - la($i) - 2 * $gap) / 3
def k0($i, $j) = la($i) + $j * (seg($i) + $gap)
def k1($i, $j) = k0($i, $j) + seg($i)
# box knuckles (j = 0 and 2) with a slab back to the wall; sockets for the lid cones
def box_knuckle($i, $j) = tomb(k0($i, $j), k1($i, $j)) + slab(k0($i, $j), k1($i, $j), $yb - 1, 0, 0, $hinge_z)
$box_knuckles = union [box_knuckle($i, 0) + box_knuckle($i, 2) for $i in range($n)]
$sockets = union [cone_pos($rc + $gap, k1($i, 1) - $e) + cone_neg($rc + $gap, k0($i, 1) + $e) for $i in range($n)]
$box = ($shell - $cavities + $box_knuckles) - $sockets
assert $box.solids == 1
# --- Lids, open 180 deg (upside down on the bed, y > 0) -----------------------------
$lip_t = 1.2
$yl = $R + $gap # mirror of the box face: the box tombs (y up to R) clear the lid
def plate($i) = slab(la($i), lb($i), $yl + $gap, $yl + $D, 0, $lid_t) # starts clear of the box knuckles
def lip_outer($i) = slab($i * $pitch + $wall + $lip_gap, $i * $pitch + $wall + $cw - $lip_gap, $yl + $wall + $lip_gap, $yl + $D - $wall - $lip_gap, $lid_t - 0.01, $lid_t + 2.5)
def lip_inner($i) = slab($i * $pitch + $wall + $lip_gap + $lip_t, $i * $pitch + $wall + $cw - $lip_gap - $lip_t, $yl + $wall + $lip_gap + $lip_t, $yl + $D, $lid_t - 0.02, $lid_t + 3)
def lip($i) = lip_outer($i) - lip_inner($i) - slab($i * $pitch, ($i + 1) * $pitch + $wall, 0, $yl + $wall + 4, $lid_t - 0.03, $lid_t + 3)
def lid_knuckle($i) = tomb(k0($i, 1), k1($i, 1)) + slab(k0($i, 1), k1($i, 1), 0, $yl + 1, 0, $lid_t)
+ cone_pos($rc, k1($i, 1) - $e) + cone_neg($rc, k0($i, 1) + $e)
# the day name, built on the closed lid and swung open with it
def mark($i) = text $days[$i] 4 at:(($i + 0.5) * $pitch + $wall / 2, $yb - $D / 2 - 2) | extrude 1
| translate 0 0 ($H + $lid_t - 0.5) | rotate 180 0 0 origin:(0, 0, $hinge_z)
def lid($i) = plate($i) + lip($i) + lid_knuckle($i) - mark($i)
$lids = union [lid($i) for $i in range($n)]
assert $lids.solids == $n # lids do not touch each other
# --- Checks: lid 0 closed clears the box; the print layout keeps 8 separate solids ----
def closed($s) = $s | rotate 180 0 0 origin:(0, 0, $hinge_z)
$lid0_closed = closed(lid(0))
$lip0_closed = closed(lip(0))
assert clear($lid0_closed, $box)
assert $lip0_closed.bbox.min[2] ~ ($H - 2.5) tol:0.1 # the lip reaches 2.5 mm into the opening
assert $lip0_closed.bbox.max[2] ~ $H tol:0.1
$print = $box + $lids
assert $print.solids == $n + 1
assert $print.bbox.x ~ $L
assert $print.bbox.y ~ (2 * ($D + $R)) tol:1.5
assert $print.bbox.z ~ $H
print "box" $box.bbox
print "print layout" $print.bbox
$print