(27)振子の運動 8(ブランコ、parametric excitation)

振子の棒の長さをリアルタイムに変えられるようにした。
タイミングをうまく合わせると、しだいに振幅が大きくなって、しまいには廻り始める。


function on.resize()
   w = platform.window:width() -- 画面幅を取得
   h = platform.window:height() -- 画面高さを取得
   center = {x = w/2, y = h/2}  -- 画面中央を支点座標とする
   length = 0.5 * h/2   -- 画面上の棒の長さ
   radius = h/30 -- 画面上の重りの半径 
   diameter = radius * 2 -- 画面上の重りの直径
   platform.window:invalidate()
end

local g = 9.80665 --重力加速度〔m/s/s〕
local L = 1 -- 棒の長さ〔m〕
local tMin = 0 -- スタート時間 〔s〕
local tMax = 0.02 -- ストップ時間〔s〕
local angleInit = 1 -- 角度初期値〔rad〕
   local aI = angleInit -- リセット用に角度初期値を保存しておく
local velInit = 0 -- 角速度初期値〔rad/s〕
   local vI = velInit -- リセット用に角速度初期値を保存しておく
local k = 0.1 -- 空気抵抗係数
local step = tMax -- 時間ステップ〔s〕 
local tol = 1e-3 -- 許容差
local elapsedTime = 0 -- シム内経過時間初期値
   
cursor.set("animate")

function on.paint(gc)
   --「今の角度、角速度」を初期値としてルンゲクッタ法(rk23())で「次の角度、角速度」を計算する
   -- rk23() からは下の行列で計算結果が返ってくる。
   -- [1][1]今の時間     [1][2]次の時間
   -- [2][1]今の角度     [2][2]次の角度
   -- [3][1]今の角速度   [3][2]次の角速度
   local mat = math.eval("rk23({vel,((−"..g..")/("..L.."))*sin(θ)-"..k.."*vel},t,{θ,vel},{"..tMin..","..tMax.."},{"..angleInit..","..velInit.."},"..step..","..tol..")")
   print(mat[2][1], mat[2][2], mat[3][1], mat[3][2])
   
   -- 重りの現在位置を計算する
   local now = {x = center.x + L * length * math.sin(mat[2][1]), y = center.y + L * length * math.cos(mat[2][1])}
      
   -- 支点から重りの位置まで直線を引く。
   gc:drawLine(center.x, center.y, now.x, now.y)
   
   -- 重りの円を描く。
   gc:fillArc(now.x - radius, now.y - radius, diameter, diameter, 0, 360)   
   
   -- シム内の経過時間を表示する。
   gc:drawString("time: "..math.floor(elapsedTime).." s", 5, 0)
   elapsedTime = elapsedTime + step
         
   -- 現在の棒の長さを表示する。
   gc:drawString("L = "..L.." m", 5, 20)

   -- 次の角度、角速度を初期値に入れ換える。
   angleInit = mat[2][2]
   velInit = mat[3][2]
end

function on.mouseMove(xm, ym) -- マウスポインターの横方向の位置で棒の長さを変える。真ん中で一番短くし、左右いずれかに離れるほど長くなるようにする。 
   L = 0.8 + math.abs(xm - center.x)/center.x -- 画面幅を 1.8 ~ 0.8 ~ 1.8 の範囲にマッピングする。それを L とする。
end
function on.escapeKey() -- escape キーでリセット
   angleInit = aI
   velInit = vI
   elapsedTime = 0
   platform.window:invalidate()
   timer.stop()
end
function on.enterKey() -- enter キーでタイマーをスタート
   timer.start(step)
end
function on.timer()
   platform.window:invalidate()
end