鳥の水笛
water bird 水を入れて吹くとピロピロ音が鳴ります。
# 鳥の水笛 — 吹いた息を全部水にくぐらせてから、頭の中の小さな笛で鳴らす
# 息の通り道: しっぽ(平たい吹き口)→ まっすぐ 45° に下りる吹き込み管 → 床ぎわで水の中へ出て泡になる
# 吹くときは鳥を立てたまま、斜め上から吹き口をくわえる
# → 水面の上の空気の部屋 → 首の中のウィンドウェイ → 頭の中の共鳴室とエッジ → 後頭部の窓から外へ
# 泡がはじけるたびに笛へ行く息の圧力が揺れて、音が震える(さえずり)。
# 水は吹き口から注ぐ(吹き込み管がまっすぐ胴の底へ通じている)。
@param 18..28 desc:"胴の長さの半分 (mm)"
body_a = 26
@param 14..22 desc:"胴の幅の半分 (mm)"
body_b = 20
@param 13..21 desc:"胴の高さの半分 (mm)"
body_c = 19
@param 1.2..2.4 step:0.1 desc:"肉厚 (mm)。0.4ノズルで4周 = 1.6"
wall = 1.6
@param 3.5..6 step:0.5 desc:"笛の共鳴室の内半径 (mm)。小さいほど高い音"
cham_r = 5.5
@param 3..7 step:0.5 desc:"胴の中の吹き込み管(ストロー)の内径 (mm)。泡の大きさが変わる"
straw_d = 5
@param 0.6..1.6 step:0.1 desc:"ウィンドウェイの隙間 (mm)"
wh = 1.0
@param 1.5..4 step:0.1 desc:"窓の長さ = ウィンドウェイ出口からエッジ先端まで (mm)"
win = 2.2
@param 20..45 desc:"エッジの傾斜 (度)"
bevel = 35
@param 4..12 step:0.5 desc:"水位 = 床からの高さ (mm)。吹き込み管の出口(床から3mm)より上に"
water_h = 8
@param choices:["bird", "cavity", "water"] desc:"出力する形: 鳥 / 中の空洞 / 水"
show = "bird"
k = body_a / 26 # 頭・翼などの飾りの大きさの倍率
# 胴の底は、卵形の側面がちょうど 45° になる高さで平らに切る。
# 楕円 (x/a)² + (z/c)² = 1 の傾きが 1 になるのは z = −c² / √(a² + c²)
zc = body_c * body_c / sqrt(body_a * body_a + body_c * body_c) # 胴の中心の高さ
floor_t = wall # 床の厚み
def egg($a, $b, $c) = sphere 1 | scale $a $b $c | translate 0 0 zc
def yslab($pts, $w) = workplane XZ | polyline $pts | extrude $w | translate 0 (-$w / 2) 0
def ycyl($r, $l) = cylinder $r $l | rotate 90 0 0
def body_y($x, $z) = body_b * sqrt(1 - ($x / body_a) ** 2 - (($z - zc) / body_c) ** 2)
def above($z) = box 400 400 200 | floor | translate 0 0 $z
# --- 胴の中: 水と空気の部屋 --------------------------------------------------
# 内側の卵形を、床の上面と平らな天井ではさむ。天井は、卵形の断面の短い方(Y)の幅が
# span になる高さに置く(そこから上の寝たドームは平らなブリッジにする)
span = 16
in_a = body_a - wall
in_b = body_b - wall
in_c = body_c - wall
z_flat = zc + in_c * sqrt(1 - (span / 2 / in_b) ** 2)
$inner = egg(in_a, in_b, in_c)
$cavity = $inner * (box 400 400 (z_flat - floor_t) | floor | translate 0 0 floor_t)
# --- しっぽ = 吹き口: 平たい尾羽の形で、吹き口から 45° にまっすぐ下りて床に届く ----------------
# 外側は平たい尾羽の断面(角を丸めた長円、厚さ t_out)で、胴を出てから先は扇形に広がる。
# 中はストロー(内径 straw_d)が先から底まで一定の太さで通る。曲がり角がないので、下面は 45° のままサポートなしで印刷できる。
# 横向きの軸(ローカル +Z)で作って y 軸まわりに −45° 起こし、吹き口を後ろ上に向ける
t_out = straw_d + 2 * 1.2 # 吹き口の厚み(中のストロー + 両側の壁 1.2)
w_root = straw_d + 6 # 胴を出るところの幅
w_tip = 15 # 吹き口の幅(扇形の先)
x_e = -0.45 * body_a # 中心線が床ぎわに届く X
z_e = floor_t + 2 # その高さ
x_tip = -body_a - 8 # 吹き口の先(中心線上)
l_tail = 4 + (x_e - x_tip) * sqrt(2) # 床の下 4mm から吹き口までの長さ
s_out = 4 + (x_e + body_a - 1) * sqrt(2) # 胴の外へ出るあたり(ここから扇形に広がる)
def stadium($w, $t) = sketch [($t / 2, -($w - $t) / 2), ($t / 2, ($w - $t) / 2),
arc ($t / 2, ($w - $t) / 2) (0, $w / 2) (-$t / 2, ($w - $t) / 2),
(-$t / 2, -($w - $t) / 2),
arc (-$t / 2, -($w - $t) / 2) (0, -$w / 2) ($t / 2, -($w - $t) / 2)]
def raise45($shape) = $shape | rotate 0 (-45) 0 | translate (x_e + 4 / sqrt(2)) 0 (z_e - 4 / sqrt(2))
# 通り道は吹き口の先から底まで、内径 straw_d の丸いストロー1本。
# 胴の中はストローの外側だけの管、胴の壁のあたり(s_join)から先は平たい尾羽の形がストローを包む
s_join = s_out - 4 # 丸い管と平たい管の切り替わり(胴の壁の内側)
$straw_out_l = cylinder (straw_d / 2 + 1.2) (s_join + 2) | floor
$straw_in_l = cylinder (straw_d / 2) (l_tail + 6) | floor | translate 0 0 (-3)
$tail_local = stadium(w_root, t_out) | loft [stadium(w_root, t_out), stadium(w_tip, t_out)] [s_out - s_join, l_tail - s_join] ruled:true
| translate 0 0 s_join
$tube_out = union [raise45($straw_out_l), raise45($tail_local)] | inter above(floor_t - 0.5)
$tube_in = raise45($straw_in_l) | inter above(floor_t)
# 出口: 管の下の端の前側の壁を、床から exit_h の高さまで切り欠く(左右の壁は脚として残る)
exit_h = 3
$tube_exit = box 10 straw_d exit_h | floor | translate (x_e + 5) 0 floor_t
# --- 頭の中の笛(ホイッスルと同じ形を小さくして、息を上向きに流す) ----------
# 笛の座標(x = 息の向き、y = 共鳴室の軸、z = 窓の向き)で作り、y 軸まわりに −90° 回して
# 息を上(+Z)、窓を後ろ(−X)に向ける。ウィンドウェイの隙間は X 方向に開くので、
# 積層の厚みではなくノズルの精度で出る
cham_l = 7 # 共鳴室の長さ(笛の y = 胴の Y)
ww = 5 # ウィンドウェイの幅
wfloor = 0.6 # ウィンドウェイ出口での床の厚み
a = wh / 2 + wfloor
kk = 2 * a / win
m = -(a * a) / win - win
qa = kk * kk + 4
qb = 2 * kk * m
qc = m * m - 4 * cham_r * cham_r
zl = (-qb + sqrt(qb * qb - 4 * qa * qc)) / (2 * qa) # 息の中心 = エッジ先端の高さ(笛の座標)
x1 = -(kk * zl + m) / 2 # エッジ先端
x0 = x1 - win # ウィンドウェイ出口
zf = zl - wh / 2
zr = zl + wh / 2
hr = 9.5 * k # 頭の半径
hx = body_a - 3 * k # 頭の中心 X
# ウィンドウェイは胴の X = hx − zl を縦に通り、下端を部屋の天井に 1.5mm 差し込む。
# 長さ lw を確保できる高さに頭を置く
xwy = hx - zl
z_in = min(z_flat, zc + in_c * sqrt(1 - (xwy / in_a) ** 2))
lw = 8 # ウィンドウェイの長さ(部屋の天井から笛まで)
hz = z_in + lw - 1.5 - x0 # 頭の中心 Z
zt_w = hr + 2 # 窓の切り欠きの外端(頭の外まで)
$w_chamber = ycyl(cham_r, cham_l)
$w_windway = yslab([(x0 - lw, zf), (x0 + 0.5, zf), (x0 + 0.5, zr), (x0 - lw, zr)], ww)
$w_window = yslab([(x0, zl - win * tan(bevel)), (x1 + (zt_w - zl) / tan(bevel), zt_w), (x0, zt_w)], ww)
$whistle_void = union [$w_chamber, $w_windway, $w_window] | rotate 0 (-90) 0 | translate hx 0 hz
# --- 頭・首・くちばし・目 ---------------------------------------------------
# 頭は球の下に 45° の円錐を付けたしずく形。首はウィンドウェイを包む最小の太さの円柱
$head = union [sphere hr, cone 0 (hr / sqrt(2)) (hr / sqrt(2)) | translate 0 0 (-hr * 1.0607)]
| translate hx 0 hz
neck_r = sqrt((wh / 2 + 1.5) ** 2 + (ww / 2 + 1.5) ** 2) + 0.5
$neck = cylinder neck_r (hz - zc) | floor | translate xwy 0 zc
# くちばし: 下面 45° で上を向き、先は半径 1.5mm に丸める(尖らせない)
xb = hx + hr - 2.5 * k # 付け根(頭に埋まる)
bl = 6 * k # 先の丸みの中心までの長さ
bh = 3.5 * k # 付け根の高さ・幅の半分
brt = 1.5 # 先の丸みの半径
bzt = hz - bh + bl + 2 * brt * 0.7071 # 丸みの中心の高さ(下面の 45° の線に接する)
$beak_side = workplane XZ
| sketch [(xb, hz - bh), (xb + bl + brt * 0.7071, bzt - brt * 0.7071),
arc (xb + bl + brt * 0.7071, bzt - brt * 0.7071) (xb + bl + brt, bzt) (xb + bl, bzt + brt),
(xb, hz + bh)]
| extrude (2 * bh + 1) | translate 0 (-bh - 0.5) 0
$beak_top = sketch [(xb, -bh), (xb + bl, -brt), arc (xb + bl, -brt) (xb + bl + brt, 0) (xb + bl, brt), (xb, bh)]
| extrude 100
$beak = $beak_side * $beak_top
ed = 3 * k
ey = sqrt(hr * hr - 2 * ed * ed)
$eyes = union [sphere (1.3 * k) | translate (hx + ed) ey (hz + ed), sphere (1.3 * k) | translate (hx + ed) (-ey) (hz + ed)]
# --- 翼: 楕円体を胴の脇から張り出し、下側を 45° の面で落とす ----------------------
wxc = -3 * k
wzc = zc + 5 * k
wy = body_y(wxc, wzc) + 0.5 * k # 楕円体の中心(胴の表面の少し外)
zs = wzc - 4 * k # 45° の面が胴から出る高さ
ys = body_y(wxc, zs)
def wing_cut($side) = box 400 400 400 | translate 0 0 200 | rotate ($side * 45) 0 0 | translate 0 ($side * ys) zs
def wing($side) = sphere 1 | scale (13 * k) (3 * k) (7 * k) | rotate 0 15 0 | translate wxc ($side * wy) wzc | inter wing_cut($side)
$wings = union [wing(1), wing(-1)]
# --- 組み立て ---------------------------------------------------------------
# 胴をくり抜いてから吹き込み管を立て、最後に通り道を抜く
$ground = box 400 400 200 | floor
$solid = union [egg(body_a, body_b, body_c), $neck, $head, $beak, $wings] | inter $ground
$voids = union [$tube_in, $tube_exit, $whistle_void, $eyes]
$bird = $solid | diff $cavity | union $tube_out | diff $voids
$room = ($cavity - $tube_out) + ($tube_in * $inner) + ($tube_exit * $inner)
$water = $room * (box 400 400 (floor_t + water_h) | floor)
if show == "cavity" then $room else (if show == "water" then $water else $bird)