Rotating-Cap Toothpick Dispenser
A toothpick dispenser made of three parts: a body, a rotating cap and a press-fit bottom plug. Turn the cap so its hole lines up with the body's hole, tip it over and shake gently: the 3 mm hole is too narrow for two picks, so they come out one at a time, guided by an off-center 45-degree funnel inside the top. Turn the cap 180 degrees to close it; the cap snaps onto the neck with a V bead and groove, and a wide groove marks the open position. The assembled dispenser measures 44 x 44 x 104.4 mm and holds about 250 standard 65 mm toothpicks; refill it by pulling out the bottom plug. Print the body upright on its open bottom, the cap upside down and the plug flange-down - no supports needed (set layout to true to get the parts in print orientation). Adjustable parameters: toothpick length and diameter, body diameter, wall thickness, rotating and press-fit clearances, hole offset and layout.
# Rotating-Cap Toothpick Dispenser (3 parts: body, rotating cap, bottom plug)
#
# Usage: turn the cap to line up its hole with the body's hole, turn it upside down and shake gently,
# and toothpicks come out one at a time through the 3mm hole (narrower than two picks, 4.4mm).
# The body's ceiling is an off-center 45deg funnel that guides the picks to the hole when inverted.
# Turn the cap 180deg to misalign the holes and close it. Refill by pulling out the bottom plug.
#
# Fits:
# cap <-> body neck radial 0.4 (moving/rotating), 0.2 gap at the top
# snap: a V bead inside the cap sits in a V groove on the neck. Radial snap-over 0.2
# bottom plug <-> body bore radial 0.1 (snug press fit)
# The parts are output assembled with gaps between them (3 solids is intended).
@param 55..90 desc:"Toothpick length (mm)"
pick_len = 65
@param 1.8..2.6 step:0.1 desc:"Toothpick diameter (mm)"
pick_d = 2.2
@param 36..60 desc:"Body outer diameter (mm)"
body_d = 44
@param 1.6..3.5 step:0.1 desc:"Wall thickness (mm)"
wall = 2.5
@param 0.2..0.6 step:0.05 desc:"Rotating clearance (radial, mm)"
rot_clear = 0.4
@param 0.05..0.3 step:0.05 desc:"Bottom plug press-fit clearance (radial, mm)"
plug_clear = 0.1
@param 4..14 desc:"Hole offset from center (mm)"
hole_off = 8
@param desc:"Lay out parts for printing (false = assembled)"
layout = false
# --- Derived dimensions ---
R = body_d / 2 # outer radius
ri = R - wall # inner radius
hole_r = (pick_d + 0.8) / 2 # hole that passes only one pick (3.0 dia)
plug_h = 5 # bottom plug insertion depth
hc = plug_h + pick_len + 2 # ceiling of the cylinder (start of the funnel)
fun_r1 = ri + hole_off # funnel (off-center cone) base radius: covers the whole inner wall
fun_h = fun_r1 - hole_r # 45deg, so height = radius difference
$top = hc + fun_h + 2 # top of the body (2mm top plate)
neck_h = 8 # length of the neck covered by the cap
rn = R - 2 # neck radius
zn = $top - neck_h # base of the neck (shoulder)
zg = $top - 4 # center of the V groove
g = 0.6 # V groove depth (half-width is the same = 45deg)
# ===== Body (revolved outline - cylindrical cavity - off-center funnel - hole) =====
body_outer = workplane XZ | sketch [
(0, 0), (R - 1, 0), (R, 1),
(R, zn), (rn, zn),
(rn, zg - g), (rn - g, zg), (rn, zg + g),
(rn, $top), (0, $top)
] | revolve Z
cav = cylinder ri (hc + 1) | floor | translate 0 0 -1
fun = cone fun_r1 hole_r fun_h | floor | translate hole_off 0 hc
| inter (cylinder ri (fun_h + 1) | floor | translate 0 0 (hc - 0.5))
$hole = cylinder hole_r 10 | floor | translate hole_off 0 ($top - 5)
body = body_outer - cav - fun - $hole
# ===== Rotating cap =====
ci = rn + rot_clear # skirt inner radius
c0 = zn + rot_clear # skirt bottom edge (gap to the shoulder)
c1 = $top + 0.2 # underside of the top plate
cap_t = 2
c2 = c1 + cap_t
cap_outer = workplane XZ | sketch [
(0, c1), (ci, c1),
(ci, zg + g), (ci - g, zg), (ci, zg - g),
(ci, c0), (R, c0),
(R, c2 - 0.8), (R - 0.8, c2), (0, c2)
] | revolve Z
# 24 grip grooves around the outside
grip = cylinder 0.8 (neck_h + 4) | floor | translate 0 0 (c0 - 1) | polar 24 (R + 0.3)
cap_hole = cylinder hole_r 10 | floor | translate hole_off 0 (c1 - 1)
# Open-position mark (one wider vertical groove on the outside, beside the hole)
mark = box 2 2 (neck_h + 4) | floor | translate (R + 0.4) 0 (c0 - 1)
cap = cap_outer - grip - cap_hole - mark
# ===== Bottom plug =====
flange_t = 2
plug = union [
cylinder R flange_t | floor | translate 0 0 (-0.2 - flange_t)
| edges <Z | chamfer 0.8,
cylinder (ri - plug_clear) plug_h | floor | translate 0 0 -0.2
]
# When laid out, orient parts for printing (cap upside down, top face on the bed)
cap_out = if layout then (cap | rotate 180 0 0 | floor | translate (R * 2.5) 0 0) else cap
plug_out = if layout then (plug | floor | translate (R * -2.5) 0 0) else plug
union [body, cap_out, plug_out]