モンキーハンティングのシミュレータ(2)
昨日のVPythonを使ったモンキーハンティングのコードを拡張して、GUI画面でパラメータの設定ができるようにした。いつものことながらwxPythonを使用。ちょっとしたモンキーハンティングのシミュレーションソフトになった。使用例を図1に示す。パラメータ設定画面と計算の可視化画面が開く。
図1:本アプリケーション使用例
まず、ファイルMonkeyHunt.pyをダブルクリックするとアプリケーションが起動する。図2が起動画面。これがパラメータの設定画面になる。
図2:パラメータ設定画面
この画面で必要な計算条件を設定する。それらは、
- ターゲットまでの距離[m]: 2つの物体の水平距離を指定する。
- ターゲットの高さ[m]: ターゲットの初期位置の地面からの高さを指定する。
- 弾丸の初期速度[m/s]:打ち出されたときの弾丸の速度の大きさを指定する。
- 時間刻み[s]:計算の時間刻みを指定する。小さすぎると計算に時間がかかり、大きすぎると正確な軌道が計算できない。
これらを設定した後、Fireボタンを押す。可視化画面が開き軌道が表示される(図3)。赤が弾丸、緑がターゲットを表す。
図3:計算結果の可視化画面
VPythonの機能で、マウスにより表示の回転や拡大が可能(図4)。
図4:表示を回転、拡大する。
赤と緑がぶつかれば、パラメータ設定画面の左下に"You got it."と表示され、はずれると
"You miss it"と表示される。
とりあえずここまで。http://python.sunnycoder.com/にファイルと説明書を置く。
ソースコードMonkeyHunt.pyは以下の通り。
# --------------------------------------------------------------------------- # program name: Monkey Hunting # file name : MonkeyHunt.py # version : 1.00 # date : Thu Jan 22 12:53:54 JST 2009 # --------------------------------------------------------------------------- import wx import os from visual import * # constant gconst = 9.8 eps = 1.0e-2 class MainFrame(wx.Frame): def __init__(self, id, title): width, height = -1, -1 wx.Frame.__init__(self, id, title="Monkey Hunting", size=wx.Size(width, height)) self.current = " " self.sb = self.CreateStatusBar(2) self.sb.SetStatusWidths([-1, 150]) self.sb.SetStatusText(self.current) Pan = wx.Panel(self, -1) Sizer0 = wx.BoxSizer(wx.VERTICAL) Sizer1 = wx.BoxSizer(wx.HORIZONTAL) Sizer2 = wx.BoxSizer(wx.HORIZONTAL) Sizer3 = wx.BoxSizer(wx.HORIZONTAL) Sizer4 = wx.BoxSizer(wx.HORIZONTAL) StTxt1 = wx.StaticText(Pan, -1, "Distance to Target [m]:") StTxt2 = wx.StaticText(Pan, -1, "Height of Target [m]:") StTxt3 = wx.StaticText(Pan, -1, "Initial velocity of bullet [m/sec]:") StTxt4 = wx.StaticText(Pan, -1, "Time step size [sec]:") self.TxtCtrl1 = wx.TextCtrl(Pan, -1, "10", size=(50, -1)) self.TxtCtrl2 = wx.TextCtrl(Pan, -1, "10", size=(50, -1)) self.TxtCtrl3 = wx.TextCtrl(Pan, -1, "10", size=(50, -1)) self.TxtCtrl4 = wx.TextCtrl(Pan, -1, "0.001", size=(50, -1)) self.RBtn = wx.Button(Pan, -1, "Fire!") self.RBtn.Bind(wx.EVT_BUTTON, self.Fire) Sizer1.Add(StTxt1, 0, wx.ALL, 2) Sizer1.Add(self.TxtCtrl1, 0, wx.ALL, 2) Sizer2.Add(StTxt2, 0, wx.ALL, 2) Sizer2.Add(self.TxtCtrl2, 0, wx.ALL, 2) Sizer3.Add(StTxt3, 0, wx.ALL, 2) Sizer3.Add(self.TxtCtrl3, 0, wx.ALL, 2) Sizer4.Add(StTxt4, 0, wx.ALL, 2) Sizer4.Add(self.TxtCtrl4, 0, wx.ALL, 2) Sizer0.Add(Sizer1, 0, wx.ALL, 2) Sizer0.Add(Sizer2, 0, wx.ALL, 2) Sizer0.Add(Sizer3, 0, wx.ALL, 2) Sizer0.Add(Sizer4, 0, wx.ALL, 2) Sizer0.Add(self.RBtn, 0, wx.ALL, 2) Pan.SetSizer(Sizer0) Sizer0.Fit(self) def Fire(self, event): dist = float(self.TxtCtrl1.GetValue()) hght = float(self.TxtCtrl2.GetValue()) ballv= float(self.TxtCtrl3.GetValue()) dt = float(self.TxtCtrl4.GetValue()) start_sim = VPython_anim(dt, dist, hght, ballv) start_sim.simulate() if start_sim.result == 0: self.sb.SetStatusText("You got it.") else: self.sb.SetStatusText("You miss it!") class VPython_anim: def __init__(self, dt, dist, hght, ballv): self.dt = dt self.dist = dist self.hght = hght self.ballv= ballv angl = atan(hght/dist) self.f0= box(pos=(0,0.5*hght,-0.5*hght), length=dist*1.2, height=hght, width=dist*1.0E-4, color=color.yellow) self.f = box(pos=(0,0,0), length=dist*1.2, height=dist*1.0E-4, width=hght, color=color.blue) self.b1 = sphere(pos=(-0.5*dist,0,0), radius=dist/50, color=color.red) self.b1.velocity = vector(ballv*cos(angl),ballv*sin(angl),0) self.b1.trail = curve(color=self.b1.color) self.b2 = sphere(pos=(0.5*dist,hght,0), radius=dist/50, color=color.green) self.b2.velocity = vector(0,0,0) self.b2.trail = curve(color=self.b2.color) self.result = 0 def simulate(self): while True: rate (100) self.b1.pos = self.b1.pos + self.b1.velocity * self.dt self.b2.pos = self.b2.pos + self.b2.velocity * self.dt self.b1.trail.append(pos=self.b1.pos) self.b2.trail.append(pos=self.b2.pos) if self.b2.pos.y < self.f.pos.y: self.b2.velocity.y = -self.b2.velocity.y else: self.b2.velocity.y = self.b2.velocity.y - gconst*self.dt self.b1.velocity.y = self.b1.velocity.y - gconst*self.dt if fabs(self.b1.pos.y - self.f.pos.y)/self.b1.pos.y < eps: self.result = -1 break if fabs(self.b1.pos.x - self.b2.pos.x)/self.b1.pos.x < eps and\ fabs(self.b1.pos.y - self.b2.pos.y)/self.b1.pos.y < eps: self.result = 0 break class Application(wx.App): def OnInit(self): frame = MainFrame(None, -1) frame.Show(True) self.SetTopWindow(frame) return True def main(): import time app = Application(0) print "info> start-up time: %s." % time.ctime(time.time()) appl_boot_path = os.getcwd() print "info> start-up directory: %s." % appl_boot_path app.MainLoop() if __name__ == '__main__': main()