Lecture 10 Keyboard and Mouse Control
建國高中特色選修課程 - 物理現象的程式設計與模擬
作者:賴奕帆
日期:2018/8/28
10-1 Keyboard Control
基本的Keyboard Control範例
請同學先執行第一個範例程式,可以按鍵盤上下左右、i、o可以控制地球的位置,按鍵盤c、r可以控制轉動軸方向,按鍵盤q、w可以控制轉動轉速。
"""
建國中學 Vpython物理模擬
作者: 物理科 賴奕帆老師
日期: 107/08/31
特色課程 Lecture 10
10_1_1_keyboard_control Example.py
"""
from vpython import * #引用視覺畫套件Vpython
angle_v = 0
pos, axis= vector(0, 0, 0), vector(0, 1, 0)
scene = canvas(width=1000, height=800, range = 6, background=vec(0.6,0.8,0.8))
ball = sphere(radius = 1.0, texture=textures.earth )
def keyinput(evt): # 呼喚keyboard使用功能
global pos, axis ,angle_v # 定義鍵盤控制參數
move = {'left': vector(-0.1, 0, 0), 'right': vector(0.1, 0, 0), 'up': vector(0, 0.1, 0),
'down': vector(0, -0.1, 0), 'i' : vector(0, 0, -0.1), 'o': vector(0, 0, 0.1)}
axel = {'c' : vector(0.00, 0, 0.02), 'r': vector(-0.02, 0, 0)}
key_s = {'q' :0.01, 'w' : -0.01}
s = evt.key #控制鍵盤事件參數
if s in move : pos = pos + move[s]
if s in axel: axis = axis + axel[s]
if s in key_s : angle_v = angle_v + key_s[s]
scene.bind('keydown', keyinput) # 將鍵盤控制設定綁定於scene視窗
while True:
rate(200)
ball.rotate(angle=angle_v, axis=axis)
ball.pos = pos
Keyboard Control_練習1
下述程式碼執行後會看到一顆黃球在進行圓周運動,目前的圓周半徑R為20,轉速w為1( 每秒鐘1弧度)。
並且可以看到紅色箭頭代表速度向量、白色箭頭代表加速度向量。
"""
建國中學 Vpython物理模擬
作者: 物理科 賴奕帆老師
日期: 107/08/31
特色課程 Lecture 10
10_1_2_keyboard_control Practice_Circle_Motion with velocity and acceleration.py
"""
from vpython import * #引用視覺畫套件Vpython
R = 20 #圓周半徑
B_r = 0.1*R #球的半徑
theta = 0*pi/180 #轉動初始角度
w = 1 #轉動角速度
t=0
dt = 0.001 #時間間隔 0.001 秒
scene = canvas(width=1000, height=800, center = vec(0,0,0), background=vec(0.3,0.4,0.4), forward=vec(0,0,-1),range=60) #設定畫面
ball = sphere(radius = B_r , color=color.yellow, make_trail= True, interval=100) #畫球
ball.pos = vector( R*cos(theta), R*sin(theta), 0) #球初始位置
ball.v = vector( 0, 0, 0) #球初速
v_arrow = arrow(pos=ball.pos,axis=vec(0,0,0),shaftwidth=0.2*B_r ,color = color.red) #速度箭頭
a_arrow = arrow(pos=ball.pos,axis=vec(0,0,0),shaftwidth=0.2*B_r ,color = color.white) #加速度箭頭
while True :
rate(1000) #每一秒跑 1000 次
theta = theta + w * dt
ball.pos = vector (R*cos(theta), R*sin(theta), 0 )
v_arrow.pos = ball.pos
v_arrow.axis = cross(vector(0,0,1),ball.pos).norm() * R * w
a_arrow.pos = ball.pos
a_arrow.axis = - ball.pos.norm() * R * w * w
t = t + dt
課堂作業 10-1
請同學們嘗試合併上述程式,讓鍵盤可以控制圓周運動的圓周半徑R與轉速w,如下圖所示。
課堂作業 10-1 參考解法步驟 :
- 複製def keyinput(evt)鍵盤控制程式碼:
def keyinput(evt): # 呼喚keyboard使用功能
global pos, axis ,angle_v # 定義鍵盤控制參數
move = {'left': vector(-0.1, 0, 0), 'right': vector(0.1, 0, 0), 'up': vector(0, 0.1, 0),
'down': vector(0, -0.1, 0), 'i' : vector(0, 0, -0.1), 'o': vector(0, 0, 0.1)}
axel = {'c' : vector(0.00, 0, 0.02), 'r': vector(-0.02, 0, 0)}
key_s = {'q' :0.01, 'w' : -0.01}
s = evt.key #控制鍵盤事件參數
if s in move : pos = pos + move[s]
if s in axel: axis = axis + axel[s]
if s in key_s : angle_v = angle_v + key_s[s]
scene.bind('keydown', keyinput) # 將鍵盤控制設定綁定於scene視窗
- 修改global pos , axis , angle_v,改為R , w。
- 修改s = evt.key。
- 注意,R與w都是純量,與原本的key_s相同,不需要加vector。
def keyinput(evt): # keyboard interrupt callback function
global R, w # define the global variables that you want to change by this function
length = {'q' :0.2, 'w' : -0.2}
angle_v = {'a' : 0.1, 's': -0.1}
s = evt.key
if s in length : R = R + length[s]
if s in angle_v: w = w + angle_v[s]
scene.bind('keydown', keyinput)
Keyboard Control_練習2
- 下述程式碼執行後會看到一顆球在進行斜向拋射,並在最高點會自動分裂成兩顆球(質量設定為2:1)。
"""
建國中學 Vpython物理模擬
作者: 物理科 賴奕帆老師
日期: 107/08/31
特色課程 Lecture 10
10_1_3_keyboard_control Practice_Pjojectile motion and boom.py
"""
from vpython import * #引用視覺畫套件Vpython
g=9.8 ; size = 0.5 ; m1 =2 ; m2 =4 ; t = 0 ; dt = 0.001
v0 = 20 ; theta_0 = 60*pi/180 ; s0 = vector( -25.0, size, 0.0)
J_vector = norm(vector(1,0,0)) #爆炸衝量方向(1,0,0)為水平爆炸
J_mag = 20 #爆炸衝量強度
scene = canvas(title='拋體運動', width=1000, height=600, center=vec(0,10,0),background=vec(0.6,0.8,0.8))#設定畫面
floor = box(length=60, height=0.01, width=4, texture=textures.wood) #畫地板
ball1 = sphere(radius = size, color=color.red, make_trail = True) #畫球
ball2 = sphere(radius = size, color=color.green, make_trail = True) #畫球
ball3 = sphere(radius = 0.01*size, color=color.black, make_trail = True) #畫球
ball1.pos = s0 ; ball1.v = vector(v0*cos(theta_0), v0*sin(theta_0) , 0.0) #球初速
ball2.pos = s0 ; ball2.v = vector(v0*cos(theta_0), v0*sin(theta_0) , 0.0) #球初速
ball3.pos = s0 ; ball3.v = vector(0, 0 , 0) #球初速
#設定鍵盤按下a,使start > 0 , 開始拋射
start = 0
while ball3.pos.y>=0: #模擬直到球落地
rate(500) #每一秒跑1000次
t = t + dt
ball3.pos = (m1*ball1.pos+m2*ball2.pos)/(m1+m2) #兩球的質心
##設定鍵盤按下a,使start > 0 , 開始拋射
if t >= 0:
ball1.pos += ball1.v*dt
ball1.v += vector(0,- g*dt,0)
ball2.pos += ball2.v*dt
ball2.v += vector(0,- g*dt,0)
if ball1.v.y < 0 and start < 1: # 注意,此處衝量J_vector只能施加一次爆炸衝擊。
ball1.v += J_vector*J_mag/m1 #球1受到向右的衝量
ball2.v += - J_vector*J_mag/m2 #球2受到向左的衝量
start = start + 1 #使衝量只會受到一次
課堂作業 10-2
請同學們設定鍵盤控制,讓程式執行時,改為可以由鍵盤控制發射與炸開的時機。
課堂作業 10-2 參考解法步驟 :
- 複製def keyinput(evt)鍵盤控制程式碼:
- 修改global pos , axis , angle_v,改為start。
- 修改s = evt.key。
def keyinput(evt): # 呼喚keyboard使用功能
global start # 定義鍵盤控制參數
key_s = {'a' :1.0}
s = evt.key #控制鍵盤事件參數
if s in key_s : start = start + key_s[s]
scene.bind('keydown', keyinput) # 將鍵盤控制設定綁定於scene視窗
- 修改執行迴圈中球射出的設定,原為"if t >= 0:",改為"if start > 0:"
- 修改執行迴圈中球爆炸的設定,原為"if ball1.v.y < 0 and start < 1: “,改為"if start > 1.9 and start <2.1”
##設定鍵盤按下a,使start > 0 , 開始拋射
if start > 0:
ball1.pos += ball1.v*dt
ball1.v += vector(0,- g*dt,0)
ball2.pos += ball2.v*dt
ball2.v += vector(0,- g*dt,0)
if start > 1.9 and start <2.1: # 注意,此處衝量J_vector只能施加一次爆炸衝擊。
ball1.v += J_vector*J_mag/m1 #球1受到向右的衝量
ball2.v += - J_vector*J_mag/m2 #球2受到向左的衝量
start = start + 1 #使衝量只會受到一次
10-2 Mouse Control
基本的Mouse Control範例
- 執行下述範例,可以用滑鼠按箭頭的頭改變向量的方向與大小。
用滑鼠按箭頭的尾改變向量的位置。
"""
建國中學 Vpython物理模擬
作者: 物理科 賴奕帆老師
日期: 107/08/31
特色課程 Lecture 10
10_2_1_mouse_control Example.py
"""
from vpython import * #引用視覺畫套件Vpython
scene.range = 8 # fixed size, no autoscaling , 固定螢幕大小
arr = arrow(pos=vec(2,0,0),axis=vec(0,5,0))
by = 1 # touch this close to tail or tip, 滑鼠按下後與尾與箭頭的判斷距離
drag = None # have not selected tail or tip of arrow, 判斷是否選擇了尾與箭頭
drag_pos = vector (0,0,0)
new_pos = vector (0,0,0)
while True:
rate(30) #以每1/30秒的週期執行下列指令
m_ev = scene.waitfor('click mousedown mouseup mousemove') #滑鼠event
if m_ev.event == 'mousedown': #如果滑鼠按下左鍵
if mag(arr.pos-m_ev.pos) <= by: #若滑鼠位置與尾端的位置差小於by
drag = 'tail' # near tail of arrow, 則判斷正在拖曳tail(尾)
elif mag((arr.pos+arr.axis)-m_ev.pos) <= by: #若滑鼠位置與箭頭的位置差小於by
drag = 'tip' # near tip of arrow, 則判斷正在拖曳tip(箭頭)
drag_pos = m_ev.pos # save press location , 紀錄按下的位置於drag_pos(注意,此時滑鼠左鍵持續按著, 可拖曳)
elif m_ev.event == 'mouseup': # released at end of drag, 若滑鼠左鍵放開
drag = None # end dragging (None is False) , 則判斷停者拖曳
if drag: #如果滑鼠正在按下拖曳tail or tip)
new_pos = m_ev.pos #把目前滑鼠的位置紀錄於new_pos
if new_pos != drag_pos: # if mouse has moved, 如果滑鼠拖曳了, 導致目前滑鼠的位置與之前紀錄的位置不一樣
displace = new_pos - drag_pos # how far , 則計算滑鼠移動了多少距離
drag_pos = new_pos # update drag position, 並更新drag_pos於目前的滑鼠位置
if drag == 'tail': #如果判斷指令為tail(尾)
arr.pos = m_ev.pos # displace the tail, 則更新尾巴的位置
if drag == 'tip': #如果判斷指令為tip(箭頭)
arr.axis += displace # displace the tip, 則更新向量的長度
print (drag, drag_pos, new_pos)
Mouse Control_範例2
- 下述程式碼執行後,會看到一個紅球,有個黃色箭頭代表出速度。
- 按a後會看到球體進行拋體運動,並繪出各個瞬間的曲率半徑。
- 也可以先用滑鼠改變球的初速度,看看不同拋射狀況下的曲率半徑如何變化。
"""
建國中學 Vpython物理模擬
作者: 物理科 賴奕帆老師
特色課程 Lecture 10
10_2_2 mouse control Practice_Projectile motion_aN and curvature radius
先用滑鼠控制初速度,按a後開始拋射運動
"""
from vpython import * #引用視覺畫套件Vpython
by = 1 # touch this close to tail or tip, 滑鼠按下後與尾與箭頭的判斷距離
drag = None # have not selected tail or tip of arrow, 判斷是否選擇了尾與箭頭
drag_pos = vector (0,0,0)
new_pos = vector (0,0,0)
g=10 ; size = 0.5 ; t=0 ; dt = 0.001 ; N =0 ; drag = None
scene = canvas(width=1000, height=600, center = vec(20,0,0), background=vec(0.4,0.6,0.6),range=25)#設定畫面
floor = box(pos = vector(50,0,0), length=100, height=0.2, width=4, texture=textures.wood) #畫地板
ball = sphere(radius = size, color=color.red, make_trail= True) #畫球
ball.pos = vector( 0, size, 0.0) ; ball.v = vector(12,16,0) #球初速
v_arrow = arrow(pos=ball.pos,axis=ball.v,shaftwidth=0.3 ,color = color.yellow)
#設定鍵盤按下a,開始拋射
start = 0
def keyinput(evt):
global start
key_s = {'a' :1}
s = evt.key
if s in key_s : start = start + key_s[s]
scene.bind('keydown', keyinput)
while ball.pos.y >= size: #模擬直到球落地 即y=球半徑
rate(1000) #每一秒跑1000次
if start <1 :
m_ev = scene.waitfor('click mousedown mouseup mousemove keydown') #滑鼠event
if m_ev.event == 'mousedown': #如果滑鼠按下左鍵
if mag((v_arrow.pos+v_arrow.axis)-m_ev.pos) <= by: #若滑鼠位置與尾端的位置差小於by
drag = 'tip' # near tip of arrow, 則判斷正在拖曳tip(箭頭)
drag_pos = m_ev.pos # save press location , 紀錄按下的位置於drag_pos(注意,此時滑鼠左鍵持續按著, 可拖曳)
elif m_ev.event == 'mouseup': # released at end of drag, 若滑鼠左鍵放開
drag = None # end dragging (None is False) , 則判斷停者拖曳
elif m_ev.event =='keydown':
drag = None
if drag: #如果滑鼠正在按下拖曳tail or tip)
new_pos = m_ev.pos #把目前滑鼠的位置紀錄於new_pos
if new_pos != drag_pos: # if mouse has moved, 如果滑鼠拖曳了, 導致目前滑鼠的位置與之前紀錄的位置不一樣
displace = new_pos - drag_pos # how far , 則計算滑鼠移動了多少距離
drag_pos = new_pos # update drag position, 並更新drag_pos於目前的滑鼠位置
if drag == 'tip': #如果判斷指令為tip(箭頭)
v_arrow.axis += displace # displace the tip, 則更新向量的長度
ball.v = v_arrow.axis
#設定當鍵盤按下a,開始拋射
if start>=1:
t=t+dt ; ball.pos = ball.pos+ball.v*dt
ball.v.y += - g*dt ; v_arrow.pos = ball.pos ; v_arrow.axis = ball.v
if N==0 or N>=0.4 : #每0.4秒畫一個曲率
v_g_sin_angle = mag(cross(ball.v,vec(0,g,0)))/(mag(ball.v)*g)
ring_radius = mag(ball.v)**2/(g*v_g_sin_angle)
circle = ring(radius = ring_radius, pos = ball.pos+ring_radius*norm(cross(ball.v,vec(0,0,1))), axis=vec(0,0,1),thickness=0.05, color=color.white )
ball_n = sphere(pos = ball.pos, radius = 0.7*size, color=color.blue)
ball_0 = sphere(pos = ball.pos+ring_radius*norm(cross(ball.v,vec(0,0,1))), radius = 0.7*size, color=color.black)
r_arrow = arrow(pos=ball.pos+ring_radius*norm(cross(ball.v,vec(0,0,1))),axis=ring_radius*norm(cross(ball.v,vec(0,0,-1))), shaftwidth=0.1, color = color.red)
N=0
N=N+dt
Mouse Control_練習1
執行後,會看到地球與許多的紅色箭頭,代表空間中地球的重力場,但還有一個奇怪的藍色箭頭。
"""
建國中學 Vpython物理模擬
作者: 物理科 賴奕帆老師
特色課程 Lecture 10
10_2_3 mouse control Practice_Gravity force.py
"""
from vpython import * #引用視覺畫套件Vpython
G = 6.67 ; M = 6*10**1 ; Re = 10 ; t =0 ; dt = 0.001
def ag(x):
return -G*M/(x**2)
scene = canvas(width=1000, height=800, center=vec(0,-5,0),
background=vec(0.6,0.8,0.8),range=6*Re)
earth = sphere(pos=vec(0,0,0), radius=Re, texture=textures.earth)
arrowlist = []
for N1 in range(-5,6,1): #X軸
for N2 in range(-5,6,1):#Y軸
arrowlist.append(arrow(pos=vector(N1*Re,N2*Re,0),axis=vector(5,0,0),shaftwidth=1 , color=color.red))
arr = arrow(pos=vector(12.5,12.5,0),axis=vector(5,0,0),shaftwidth=1 , color=color.blue)
for N in arrowlist:
N_dist = mag(N.pos - earth.pos)
N_radiavector = norm(N.pos-earth.pos)
if N_dist > 1.2*Re and N_dist < 5*Re:
N.axis = ag(N_dist) * N_radiavector * 10
else: N.axis = vector(0,0,0)
while True:
rate(1000)
課堂作業 10-3
請同學們利用滑鼠控制,能隨意控制藍色箭頭的位置,並讓藍色箭頭能表現出其位置的重力場向量。
`
`
課堂作業 10-3 參考解法步驟 :
- 在執行迴圈處,讓藍色箭頭arr能在迴圈中即時計算地球引力場強度。
while True:
rate(1000)
arr_dist = mag(arr.pos - earth.pos)
arr_radiavector = norm(arr.pos - earth.pos)
if arr_dist > 1.0*Re :
arr.axis = ag(arr_dist) * arr_radiavector * 10
else: arr.axis = vector(0,0,0)
- 複製mouse control中的滑鼠控制程式碼:
包含基本參數 by , drag , drag_pos 與 new_pos。
by = 1 # touch this close to tail or tip, 滑鼠按下後與尾與箭頭的判斷距離
drag = None # have not selected tail or tip of arrow, 判斷是否選擇了尾與箭頭
drag_pos = vector (0,0,0)
new_pos = vector (0,0,0)
和執行迴圈處,所以即時控制滑鼠的部分
m_ev = scene.waitfor('click mousedown mouseup mousemove') #滑鼠event
if m_ev.event == 'mousedown': #如果滑鼠按下左鍵
if mag(arr.pos-m_ev.pos) <= by: #若滑鼠位置與尾端的位置差小於by
drag = 'tail' # near tail of arrow, 則判斷正在拖曳tail(尾)
elif mag((arr.pos+arr.axis)-m_ev.pos) <= by: #若滑鼠位置與箭頭的位置差小於by
drag = 'tip' # near tip of arrow, 則判斷正在拖曳tip(箭頭)
drag_pos = m_ev.pos # save press location , 紀錄按下的位置於drag_pos(注意,此時滑鼠左鍵持續按著, 可拖曳)
elif m_ev.event == 'mouseup': # released at end of drag, 若滑鼠左鍵放開
drag = None # end dragging (None is False) , 則判斷停者拖曳
if drag: #如果滑鼠正在按下拖曳tail or tip)
new_pos = m_ev.pos #把目前滑鼠的位置紀錄於new_pos
if new_pos != drag_pos: # if mouse has moved, 如果滑鼠拖曳了, 導致目前滑鼠的位置與之前紀錄的位置不一樣
displace = new_pos - drag_pos # how far , 則計算滑鼠移動了多少距離
drag_pos = new_pos # update drag position, 並更新drag_pos於目前的滑鼠位置
if drag == 'tail': #如果判斷指令為tail(尾)
arr.pos = m_ev.pos # displace the tail, 則更新尾巴的位置
if drag == 'tip': #如果判斷指令為tip(箭頭)
arr.axis += displace # displace the tip, 則更新向量的長度
- 由於我們這裡使需要控制藍色箭頭的Tail,所以滑鼠控制的部分不需要改變tip長度,因此可以把部分程式碼加上##(alt+3),當然,其實不刪除其實也可以。
## elif mag((arr.pos+arr.axis)-m_ev.pos) <= by: #若滑鼠位置與箭頭的位置差小於by
## drag = 'tip' # near tip of arrow, 則判斷正在拖曳tip(箭頭)
## drag_pos = m_ev.pos # save press location , 紀錄按下的位置於drag_pos(注意,此時滑鼠左鍵持續按著, 可拖曳)
## if drag == 'tip': #如果判斷指令為tip(箭頭)
## arr.axis += displace # displace the tip, 則更新向量的長度
Scene bind mouse
這堂課的最後,介紹一個有趣的滑鼠控制功能。
執行下述程式後,畫面中會出現一個藍球。
但若用滑鼠在螢幕中按下,藍球的顏色會改變,且滑鼠按下的位置也會出現一顆綠球。
from vpython import *
s = sphere(color=color.cyan)
def change(ev):
if s.color.equals(color.cyan):
s.color = color.red
else:
s.color = color.cyan
sphere(pos=ev.pos, radius=0.2, color=color.green)
scene.bind('click', change)
這功能有什麼用呢,聰明的同學應該已經想到了,在Lecture9中,曾經有過電力線的討論,若我們利用這個功能,就可以隨意在電力線中加入隨意的粒子,看看它們在場中會如何的運動。
最後就不當作業了,直接當範例給大家參考吧,大家辛苦了,希望都能持續精進,加油吧。
"""
建國中學 Vpython物理模擬
作者: 物理科 賴奕帆老師
日期: 107/08/31
特色課程 Lecture 10
10_2_4_mouse_control Example_Electric field simulation.py
"""
from vpython import * #引用視覺畫套件Vpython
k = 9*10**9
size = 0.4 # charge size
t = 0 ; dt = 0.001
b_N = 20
Q1_charge = 1*10**(-5) #Q1電量
Q1_position = vector(-2, 0, 0) #Q1位置
Q2_charge = -1*10**(-5) #Q2電量
Q2_position = vector(2, 0, 0) #Q2位置
q_charge = 1 * 10 **(-7)
q_position = vector (1 , 1 , 0)
q_m = 10**(-3)
q_v = vector (0 , 0 , 0)
scene = canvas(title='dipole', height=600, width=1000, range=5.0,
auto_scale=False, background=vec(1.0,1.0,1.0))
Q1 = sphere(pos = Q1_position , radius = size , color = color.blue)
Q2 = sphere(pos = Q2_position , radius = size , color = color.red)
field_ball_1=[]
for N in range(0,b_N,1):#build field ball from wall
field_ball_1.append(sphere(pos=vector(size*cos(2*pi*N/b_N), size*sin(2*pi*N/b_N),0)+Q1_position,
radius=0.01, color=vec(1,1,0), make_trail=True, v=vector(0,0,0)))
field_ball_2=[]
for N in range(0,b_N,1):#build field ball from wall
field_ball_2.append(sphere(pos=vector(size*cos(2*pi*N/b_N), size*sin(2*pi*N/b_N),0)+Q2_position,
radius=0.01, color=vec(0.8,0.8,0.3), make_trail=True, v=vector(0,0,0)))
def Force_E(r, q):#force of field
r1 = r - Q1_position
r2 = r - Q2_position
return k*q*Q1_charge*r1.norm()/(r1.mag*r1.mag)+k*q*Q2_charge*r2.norm()/(r2.mag*r2.mag)
q = []
def make_q_charge(evt):
loc = evt.pos
print ("click at ", loc)
q.append(sphere(pos=loc, radius=0.2*size, color=color.green, make_trail=True,
v=vector(0,0,0)))
scene.bind('mousedown', make_q_charge)
while True:
rate(1000)
for N in field_ball_1:
N.v = Force_E(N.pos, 1.0).norm()
N.pos += N.v*dt
for N in field_ball_2:
N.v = Force_E(N.pos, -1.0).norm()
N.pos += N.v*dt
for N in q:
if mag(N.pos-Q1_position)>=size and mag(N.pos-Q2_position)>=size :
N.v = N.v + Force_E(N.pos, q_charge)/q_m *dt
N.pos = N.pos+N.v*dt
else : N.pos = N.pos
本單元課程自2018.7.1日起已被瀏覽 585 次