Go Stone Bowl
A lidded bowl for go stones, shaped like a traditional go bowl with its bulge below the middle. Give it the number of stones (181 black or 180 white for a full set) and their size, and the height is computed from the stone volume at 60 % packing; the cavity volume is then measured and checked against it. The lid is a shallow dish that sits in a rebate at the mouth and, turned over beside the board, holds captured stones. The default bowl for 181 stones of 22 x 9 mm is 126 mm across and 80 mm tall, with a 102 mm lid; the print layout is 237 x 126 x 80 mm. Both parts print open side up with no supports, the bulge staying under 45 deg by construction. Stone count and size, the widest diameter and the wall thickness are parameters.
# Go Stone Bowl - a lidded bowl sized from the stone count, the lid holds captured stones
#
# The bowl is a revolved profile that bulges below the middle like a traditional go
# bowl. Its height is computed from the number and size of the stones (ellipsoid
# volume per stone, 60 % packing), and the cavity volume is measured and checked
# against that. The lid is a shallow dish that sits in a rebate at the mouth and,
# turned over beside the board, holds the prisoners. Two parts, both printed open
# side up with no supports: the bulge is kept under 45 deg by construction.
@param 150..200 step:1 group:"Stones" desc:"Stones the bowl must hold (181 black or 180 white for a full set)"
$count = 181
@param 20..23 step:0.5 group:"Stones" desc:"Stone diameter (mm)"
$stone_d = 22
@param 6..11 step:0.5 group:"Stones" desc:"Stone thickness (mm)"
$stone_t = 9
@param 100..160 step:2 group:"Bowl" desc:"Widest outer diameter (mm)"
$dia = 126
@param 2..4 step:0.5 group:"Bowl" desc:"Wall thickness (mm)"
$wall = 3
@param choices:["print", "closed"] group:"Output" desc:"Print layout, or the lid on the bowl"
$view = "print"
$packing = 0.6
$v_stone = pi / 6 * $stone_d * $stone_d * $stone_t
$v_need = $count * $v_stone / $packing
$floor_t = 3
$r_max = $dia / 2 - $wall # inner radius at the bulge
$r_top = 0.84 * $r_max # inner radius at the mouth
$r_floor = 0.72 * $r_max # inner radius at the floor
$z_bulge = 0.42 # the bulge sits at this fraction of the height
# mean of r^2 over the two cosine blends (floor to bulge, bulge to mouth) gives the height
def blend_r2($a, $b) = (($a + $b) / 2) * (($a + $b) / 2) + (($b - $a) / 2) * (($b - $a) / 2) / 2
$mean_r2 = $z_bulge * blend_r2($r_floor, $r_max) + (1 - $z_bulge) * blend_r2($r_top, $r_max)
$h_in = 1.02 * $v_need / (pi * $mean_r2) # 2 % margin; the measured cavity is checked below
$H = $h_in + $floor_t
# inner radius along the height: two cosine blends meeting at the bulge
def r_low($z) = $r_max - ($r_max - $r_floor) * (1 + cos(180 * ($z / $h_in) / $z_bulge)) / 2
def r_high($z) = $r_max - ($r_max - $r_top) * (1 - cos(180 * ($z / $h_in - $z_bulge) / (1 - $z_bulge))) / 2
def rin($z) = if $z / $h_in < $z_bulge then r_low($z) else r_high($z)
# steepest outward slope of the bulge (sine of the half-cosine), kept under 45 deg
assert ($r_max - $r_floor) * pi / 2 / ($z_bulge * $h_in) < 1
$n = 40
$dz = $h_in / $n
# profile: up the outside, across the rim, down the inside, across the floor
$pts = [(if $i <= $n then (rin($i * $dz) + $wall, $floor_t + $i * $dz) else (rin((2 * $n + 1 - $i) * $dz), $floor_t + (2 * $n + 1 - $i) * $dz)) for $i in range(2 * $n + 2)]
$shell = workplane XZ | polyline $pts | revolve Z
$base = cylinder ($r_floor + $wall) $floor_t | floor
$rebate_d = 2.5
$rebate = cylinder ($r_top + 1.5) ($rebate_d + 1) | floor | translate 0 0 ($H - $rebate_d)
$bowl = ($shell + $base) - $rebate
assert $bowl.solids == 1
assert $bowl.bbox.x ~ $dia
assert $bowl.bbox.z ~ $H
# the cavity: a solid of the inner profile, measured for the stone volume
$cav_pts = [(if $i == 0 then (0, $floor_t) else if $i == $n + 2 then (0, $H) else (rin(($i - 1) * $dz), $floor_t + ($i - 1) * $dz)) for $i in range($n + 3)]
$cav = workplane XZ | polyline $cav_pts | revolve Z
assert $cav.solids == 1
assert $cav.volume >= $v_need
print "stones volume (cm3)" ($count * $v_stone / 1000)
print "cavity volume (cm3)" ($cav.volume / 1000)
print "bowl height (mm)" $H
# --- Lid: a shallow dish sitting in the rebate, standing 1.5 mm proud -------------
$lid_r = $r_top + 1.5 - 0.3
$lid_h = 4
$lid = (cylinder $lid_r $lid_h | floor | faces >Z | fillet 1)
- (cylinder ($lid_r - 2) ($lid_h - 1.5) | floor | translate 0 0 1.5)
assert $lid.solids == 1
assert $lid.bbox.x ~ (2 * $lid_r)
$lid_on = $lid | translate 0 0 ($H - $rebate_d)
assert clear($bowl, $lid_on)
$closed = union [$bowl, $lid_on]
assert $closed.bbox.z ~ ($H - $rebate_d + $lid_h)
$print = union [$bowl, $lid | translate ($dia / 2 + $lid_r + 8) 0 0]
assert $print.solids == 2
if $view == "closed" then $closed else $print