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