Draining Soap Dish
A rounded soap dish whose ribs run front to back and slope down toward the front, so the bar sits on dry rib tops while the water runs along the ribs and the sloped floor to a spout cut in the front wall. Set it on the edge of the sink with the spout over the basin and the soap stays dry between uses. The default measures 110 x 80 x 16 mm with eleven ribs and prints flat on its base with no supports; the spout is open to the top, so nothing bridges. Adjustable parameters: length, depth, wall height and thickness, rib pitch, thickness and drop, and spout width.
# Soap Dish - sloped ribs lift the soap, the floor and ribs drain to a spout at the front
#
# A rounded tray whose ribs run front to back and slope down toward the front, so the
# soap sits on dry rib tops and the water runs along the ribs and the sloped floor to a
# spout cut in the front wall. Set it on the sink edge with the spout over the basin.
# Prints flat on its base with no supports (the spout is open to the top).
@param 80..160 step:5 group:"Size" desc:"Length, side to side (mm)"
$len = 110
@param 60..120 step:5 group:"Size" desc:"Depth, front to back (mm)"
$dep = 80
@param 12..25 step:1 group:"Size" desc:"Wall height (mm)"
$h = 16
@param 1.6..3 step:0.2 group:"Size" desc:"Wall thickness (mm)"
$wall = 2
@param 6..14 step:1 group:"Ribs" desc:"Rib pitch (mm)"
$rib_pitch = 9
@param 2..4 step:0.5 group:"Ribs" desc:"Rib thickness (mm)"
$rib_t = 3
@param 2..6 step:0.5 group:"Ribs" desc:"Drop of each rib top from back to front (mm)"
$rib_drop = 4
@param 16..40 step:2 group:"Drain" desc:"Spout width in the front wall (mm)"
$spout_w = 24
$floor_back = 4 # floor top at the back wall
$floor_front = 2 # floor top at the front wall (spout level)
$rib_back = $floor_back + 6 # rib top at the back
$rib_front = $rib_back - $rib_drop
$yi = $dep / 2 - $wall # inner half depth
$xi = $len / 2 - $wall
# --- Tray: rounded outer shell, inner cavity down to a sloped floor ---------------
$outer = box $len $dep $h center:(true, true, false) | edges =Z | fillet 10 | edges >Z | fillet 1
$cavity = box (2 * $xi) (2 * $yi) ($h + 2) center:(true, true, false) | edges =Z | fillet 8
# the floor is a wedge: profile (y, z) extruded along X and stood up
$floor = polyline [(-$yi - 1, 0), ($yi + 1, 0), ($yi + 1, $floor_back), (-$yi - 1, $floor_front)]
| extrude (2 * $xi + 2) | rotate 90 0 90 | translate (-$xi - 1) 0 0
$tray = ($outer - $cavity) + ($floor | inter $cavity)
# --- Ribs: wedges along Y whose tops slope to the front ---------------------------
$n = floor((2 * $xi - 6) / $rib_pitch)
$x0 = -($n - 1) * $rib_pitch / 2
$rib = polyline [(-$yi - 1, 0), ($yi + 1, 0), ($yi + 1, $rib_back), (-$yi - 1, $rib_front)]
| extrude $rib_t | rotate 90 0 90 | translate (-$rib_t / 2) 0 0
$ribs = union [$rib | translate ($x0 + $k * $rib_pitch) 0 0 for $k in range($n)] | inter $cavity
# --- Spout: open notch in the front wall down to the floor level ------------------
$spout = box $spout_w ($wall + 4) ($h + 2) center:(true, true, false) | translate 0 (-$dep / 2) $floor_front
$dish = ($tray + $ribs) - $spout
assert $dish.solids == 1
assert $dish.bbox.x ~ $len
assert $dish.bbox.y ~ $dep
assert $dish.bbox.z ~ $h
assert $n >= 5
assert $rib_front > $floor_front + 2 # ribs stay above the floor at the front
$slope = atan($rib_drop / (2 * $yi))
print "rib slope (deg)" $slope
print "ribs" $n
print "dish" $dish.bbox
$dish