作者:王一哲
第1版:2020/10/19
第2版:2020/12/5 補充不使用 NumPy 的寫法
當我需要繪製橫波的動畫時,通常是使用 GeoGebra 搭配以下的數學式
橫波上的每個質點在鉛直方向做簡諧運動,如果想要用 VPython 做出一樣的效果,理論上只要讓一排的小球每隔固定的時間差落到正下方的彈簧上,小球與彈簧結合後開始做鉛直簡諧運動即可。為了盡量避免使用 for 迴圈,我試著使用 numpy 陣列 (ndarray) 取代大部分的串列 (list),以下是我測試成功的動畫及程式碼。
"""
VPython教學: 使用多個質點的鉛直簡諧運動組成橫波
日期: 2020/10/19
作者: 王一哲
"""
from vpython import *
import numpy as np
"""
1. 參數設定, 設定變數及初始值
"""
m, r, h = 0.1, 0.3, 4 # 小球質量, 半徑, 起始高度
g = 9.8 # 重力加速度
k, L0 = 1.0, 8 # 彈簧彈性常數, 原長
T = 2*pi*sqrt(m/k) # 簡諧運動週期
t, dt = 0, 0.0005 # 時間, 時間間隔
length, num = 8, 41 # 波長, 小球個數
"""
2. 畫面設定
"""
# 動畫視窗
scene = canvas(title="Particles and Wave", width=600, height=600, x=0, y=0,
background=color.black, range=1.7*length, center=vec(1.5*length, 0, 0))
# 地板
floor = box(pos=vec(1.5*length, -L0, 0), size=vec(3*length, r, length), color=color.blue)
xs = np.linspace(0, 3*length, num) # x 坐標
ys = np.ones(num)*h # y 坐標
vs = np.zeros(num) # y 方向速度
ay = np.ones(num)*(-g) # y 方向加速度
delays = np.linspace(0, 3*T, num) # 時間延遲
states = np.full(num, False) # 小球與彈簧是否接觸
# 小球
balls = [sphere(pos=vec(xs[i], ys[i], 0), radius=r, color=color.red) for i in range(num)]
# 彈簧
springs = [helix(pos=vec(xs[i], -L0, 0), radius=0.6*r, thickness=0.4*r, axis=vec(0, L0, 0), color=color.yellow) for i in range(num)]
"""
3. 物體運動部分
"""
while True:
rate(1000)
states[ys <= 0] = True
ay[np.nonzero(states == True)] = -g - k*ys[np.nonzero(states == True)]/m
vs[delays <= t] += ay[delays <= t]*dt
ys += vs*dt
for i in range(num):
balls[i].pos = vec(balls[i].pos.x, ys[i], 0)
if states[i]:
springs[i].axis = vec(0, ys[i]+L0, 0)
t += dt
np.linspace(起始值, 結束值, 等分數量) # 將起始值到結束值之間按照數量等分並回傳為陣列
np.zeros(長度) # 依照指定的長度產生所有元素皆為0的陣列
np.ones(長度) # 依照指定的長度產生所有元素皆為1的陣列
np.full(長度, 值) # 依照指定的長度產生所有元素皆為輸入值的陣列
"""
VPython教學: 使用多個質點的鉛直簡諧運動組成橫波, 不使用 NumPy
日期: 2020/12/5
作者: 王一哲
"""
from vpython import *
"""
1. 參數設定, 設定變數及初始值
"""
m, r, h = 0.1, 0.3, 4 # 小球質量, 半徑, 起始高度
g = 9.8 # 重力加速度
k, L0 = 1.0, 8 # 彈簧彈性常數, 原長
T = 2*pi*sqrt(m/k) # 簡諧運動週期
t, dt = 0, 0.0005 # 時間, 時間間隔
length, num = 8, 41 # 波長, 小球個數
"""
2. 畫面設定
"""
# 動畫視窗
scene = canvas(title="Particles and Wave", width=600, height=600, x=0, y=0,
background=color.black, range=1.7*length, center=vec(1.5*length, 0, 0))
# 地板
floor = box(pos=vec(1.5*length, -L0, 0), size=vec(3*length, r, length), color=color.blue)
xs = [i*3*length/(num-1) for i in range(num)]
ys = [h]*num
vs = [0]*num
ay = [-g]*num
delays = [i*3*T/(num-1) for i in range(num)]
states = [False]*num
# 小球
balls = [sphere(pos=vec(xs[i], ys[i], 0), radius=r, color=color.red) for i in range(num)]
# 彈簧
springs = [helix(pos=vec(xs[i], -L0, 0), radius=0.6*r, thickness=0.4*r, axis=vec(0, L0, 0), color=color.yellow) for i in range(num)]
"""
3. 物體運動部分
"""
while True:
rate(1000)
for i in range(num):
if ys[i] <= 0:
states[i] = True
if states[i]:
ay[i] = -g - k*ys[i]/m
if delays[i] <= t:
vs[i] += ay[i]*dt
ys[i] += vs[i]*dt
balls[i].pos = vec(balls[i].pos.x, ys[i], 0)
if states[i]:
springs[i].axis = vec(0, ys[i]+L0, 0)
t += dt
主要修改以下兩處,修改後的程式碼可以在 GlowScript 網站上執行,這是 GlowScript 網站動畫連結。
由於 numpy 陣列有許多很神奇的函數及使用方法,如果能夠善加利用可以少寫很多個 for 迴圈,有效提升程式的運算速度。但是 VPython 的物件只能儲存到串列中,因此在更新整串的 VPython 物件時仍然需要使用 for 迴圈,這是無法避免的。
VPython