feat: 新增波形能量动画系统 plot_wave.py

- 创建 plot_wave.py: 从 display.txt 读取原子位移数据
  绘制纵波(x) + 横波(y) + 横波(z) 波形随时间的动画
  同时绘制系统动能/弹性势能/总能量/输入功率(dE/dt)时变曲线
  输出 wave_animation.gif
- 所有 input.txt 新增 step_plot_wave: 0 开关
- case05 开启 step_plot_wave: 1
- dynamics.py disp_data 新增 bond_stiffness/bond_rest_lengths
- 更新案例文档
This commit is contained in:
2026-06-11 12:39:46 +08:00
parent 685234c84f
commit 80520590d1
20 changed files with 1231 additions and 86 deletions
+89 -16
View File
@@ -317,10 +317,11 @@ def update_camera_info(event=None):
c = view.camera
camera_info.text = (
"Camera\n"
f"center = ({c.center[0]:.2f}, {c.center[1]:.2f}, {c.center[2]:.2f})\n"
f"distance = {c.distance:.2f}\n"
f"elevation = {c.elevation:.2f}\n"
f"azimuth = {c.azimuth:.2f}"
f"center = ({c.center[0]:.1f}, {c.center[1]:.1f}, {c.center[2]:.1f})\n"
f"distance = {c.distance:.1f}\n"
f"elevation = {c.elevation:.1f}\n"
f"azimuth = {c.azimuth:.1f}\n"
f"step = {PAN_SPEED:.1f} | {'透视' if _PERSPECTIVE else '正交'}"
)
@@ -333,6 +334,7 @@ def update_ball_info(frame_idx, x, y, z, vx, vy, vz):
f"t = {t:.2f} s | dt = {DT:.3f} s | nstep = {NSTEP}\n"
f"Position: ({x:.2f}, {y:.2f}, {z:.2f})\n"
f"Velocity: ({vx:.2f}, {vy:.2f}, {vz:.2f})\n"
f"W/S 沿Z轴 | A/D 左右 | Q/E 升降 | C/X 步长 | V 透视/正交"
)
@@ -358,28 +360,99 @@ def reposition_camera_info(event=None):
update_camera_info()
# ── 平移速度 & 投影模式(全局变量)──
PAN_SPEED = 1.0
_PERSPECTIVE = True # True=透视, False=正交
def handle_view_interaction(event):
update_camera_info()
def rotate_about_screen_normal(angle):
if hasattr(view.camera, "roll"):
view.camera.roll = (view.camera.roll + angle) % 360
else:
view.camera.azimuth = (view.camera.azimuth + angle) % 360
update_camera_info()
def handle_key_press(event):
global PAN_SPEED, _PERSPECTIVE
key_name = ""
if getattr(event, "text", None):
key_name = event.text.lower()
elif getattr(event, "key", None) is not None:
key_name = str(event.key).lower()
if key_name == "q":
rotate_about_screen_normal(-90)
elif key_name == "e":
rotate_about_screen_normal(90)
c = view.camera
# ── 投影切换 ──
if key_name == "v":
_PERSPECTIVE = not _PERSPECTIVE
try:
if _PERSPECTIVE:
c.fov = 60.0
else:
c.fov = 0.0
print(f"[draw] 投影模式: {'透视' if _PERSPECTIVE else '正交'}")
except Exception:
print("[draw] 当前相机不支持 fov 切换")
update_camera_info()
return
# ── 步长控制 ──
if key_name == "c":
PAN_SPEED = min(PAN_SPEED * 1.5, 50.0)
print(f"[draw] 步长: {PAN_SPEED:.1f}")
update_camera_info()
return
elif key_name == "x":
PAN_SPEED = max(PAN_SPEED / 1.5, 0.05)
print(f"[draw] 步长: {PAN_SPEED:.1f}")
update_camera_info()
return
# ── 计算相机方向向量 ──
import math as _math
azim_rad = _math.radians(c.azimuth)
elev_rad = _math.radians(c.elevation)
# 视线方向(从 center 指向相机)
vd = np.array([
_math.cos(elev_rad) * _math.sin(azim_rad),
_math.sin(elev_rad),
_math.cos(elev_rad) * _math.cos(azim_rad),
])
vd /= np.linalg.norm(vd)
# 屏幕右方向
world_up = np.array([0.0, 1.0, 0.0])
right = np.cross(vd, world_up)
rn = np.linalg.norm(right)
if rn > 1e-10:
right /= rn
else:
right = np.array([1.0, 0.0, 0.0])
# 屏幕上方向
up = np.cross(right, vd)
un = np.linalg.norm(up)
if un > 1e-10:
up /= un
else:
up = np.array([0.0, 1.0, 0.0])
pan = PAN_SPEED * 0.3
if key_name == "a": # 右移(屏幕右方向)
c.center = tuple(np.array(c.center) + right * pan)
elif key_name == "d": # 左移(屏幕右方向负向)
c.center = tuple(np.array(c.center) - right * pan)
elif key_name == "e": # 上升(屏幕上方向,原 W 的功能)
c.center = tuple(np.array(c.center) + up * pan)
elif key_name == "q": # 下降(屏幕上方向负向,原 S 的功能)
c.center = tuple(np.array(c.center) - up * pan)
elif key_name == "w": # 相机沿 Z 轴上移(靠近场景)
c.center = (c.center[0], c.center[1], c.center[2] + pan)
elif key_name == "s": # 相机沿 Z 轴下移(远离场景)
c.center = (c.center[0], c.center[1], c.center[2] - pan)
else:
return
update_camera_info()
def reset_camera_view():