feat: 增加驱动力系统、Marker渲染模式、动画防闪退、案例文档
- 新增 driving_force 驱动力系统(driver.txt 定义,支持周期控制) - 新增 use_marker 渲染开关(GPU实例化点精灵,提升大量原子性能) - 修复动画闪退:独立控制台、错误日志、启动存活检测 - 重绘 draw.py 架构:双渲染模式 + 预分配键线缓冲区 - 修复 raw trajectory 采样时间变量遮蔽 bug - 重构 case05: 60原子一维链 + 驱动力 + 完整案例文档 - 修复所有案例 Readme.md 编码(GBK → UTF-8) - 所有 input.txt 新增 driver_file / driving_force / use_marker 参数
This commit is contained in:
@@ -72,6 +72,9 @@ PLOT_ATOM_ROW = int(disp_data.get("plot_atom_row", 0))
|
||||
PLOT_ATOM_ID = int(disp_data.get("plot_atom_id", ATOM_IDS[0]))
|
||||
BOND_PAIRS = disp_data.get("bond_pairs", [])
|
||||
|
||||
# 渲染方式:0=Sphere(网格球体), 1=Marker(GPU点精灵)
|
||||
USE_MARKER = int(disp_data.get("use_marker", 0))
|
||||
|
||||
if N_FRAMES <= 0:
|
||||
raise ValueError(
|
||||
"output/display.txt 中没有可播放的帧,请检查 sample_start/sample_end/NSTEP 配置。")
|
||||
@@ -171,7 +174,10 @@ axes_group.append(scene.visuals.Text(text="y", color=(0.2, 1.0, 0.2, 1.0), font_
|
||||
axes_group.append(scene.visuals.Text(text="z", color=(0.3, 0.6, 1.0, 1.0), font_size=18,
|
||||
pos=(0, 0, axis_length + 0.2), anchor_x="left", anchor_y="bottom", parent=view.scene))
|
||||
|
||||
# 所有小球(每个原子一个球,不同颜色)
|
||||
# ── 原子渲染 ──────────────────────────────────
|
||||
# 两种渲染模式:
|
||||
# Sphere = 独立网格球体(精细,适合少量原子)
|
||||
# Marker = GPU 实例化点精灵(高效,适合大量原子)
|
||||
TAB10_RGB = np.array([
|
||||
[0.1216, 0.4667, 0.7059], # 蓝
|
||||
[0.8902, 0.4667, 0.1137], # 橙
|
||||
@@ -184,23 +190,41 @@ TAB10_RGB = np.array([
|
||||
[0.7373, 0.7412, 0.1333], # 黄绿
|
||||
[0.0902, 0.7451, 0.8118], # 青
|
||||
])
|
||||
balls = []
|
||||
# 每个原子的颜色(循环使用 tab10 色板)
|
||||
atom_colors = np.zeros((N_ATOMS, 4), dtype=np.float32)
|
||||
for i in range(N_ATOMS):
|
||||
r, g, b = TAB10_RGB[i % len(TAB10_RGB)]
|
||||
s = scene.visuals.Sphere(
|
||||
radius=float(ATOM_RADII[i]), method="latitude",
|
||||
color=(r, g, b, 1.0), edge_color=None, parent=view.scene)
|
||||
s.mesh.shading = "smooth"
|
||||
balls.append(s)
|
||||
atom_colors[i] = [r, g, b, 1.0]
|
||||
|
||||
if USE_MARKER:
|
||||
# ── Marker 模式:GPU 实例化,一次 draw call ──
|
||||
marker_pos = np.zeros((N_ATOMS, 3), dtype=np.float32)
|
||||
marker_sizes = np.array([float(ATOM_RADII[i]) * 40 for i in range(N_ATOMS)], dtype=np.float32)
|
||||
balls = scene.visuals.Markers(parent=view.scene)
|
||||
balls.set_data(
|
||||
pos=marker_pos, face_color=atom_colors,
|
||||
size=marker_sizes, symbol='disc', edge_width=0,
|
||||
)
|
||||
else:
|
||||
# ── Sphere 模式:每个原子一个独立网格球体 ──
|
||||
balls = []
|
||||
for i in range(N_ATOMS):
|
||||
r, g, b, _ = atom_colors[i]
|
||||
s = scene.visuals.Sphere(
|
||||
radius=float(ATOM_RADII[i]), method="latitude",
|
||||
color=(r, g, b, 1.0), edge_color=None, parent=view.scene)
|
||||
s.mesh.shading = "smooth"
|
||||
balls.append(s)
|
||||
|
||||
# 成键线(原子之间的连接)
|
||||
if len(BOND_PAIRS) > 0:
|
||||
n_bonds = len(BOND_PAIRS)
|
||||
bond_pos = np.zeros((n_bonds * 2, 3), dtype=np.float32)
|
||||
bond_pos_buffer = np.zeros((n_bonds * 2, 3), dtype=np.float32) # 预分配,后续原地修改
|
||||
bond_lines = scene.visuals.Line(
|
||||
pos=bond_pos, color=(0.7, 0.7, 0.7, 0.8), width=3,
|
||||
pos=bond_pos_buffer, color=(0.7, 0.7, 0.7, 0.8), width=3,
|
||||
connect="segments", parent=view.scene)
|
||||
else:
|
||||
bond_pos_buffer = None
|
||||
bond_lines = None
|
||||
|
||||
# 六个面形成立方体边界(每个面独立透明度,alpha<=0 时隐藏该面)
|
||||
@@ -361,19 +385,10 @@ def handle_key_press(event):
|
||||
def reset_camera_view():
|
||||
global frame_idx
|
||||
frame_idx = 0
|
||||
# 立即复位所有小球到第 0 帧
|
||||
for i in range(N_ATOMS):
|
||||
balls[i].transform = STTransform(translate=(
|
||||
float(DISP_ALL_X[frame_idx, i]),
|
||||
float(DISP_ALL_Y[frame_idx, i]),
|
||||
float(DISP_ALL_Z[frame_idx, i]),
|
||||
))
|
||||
# 立即复位所有原子到第 0 帧
|
||||
_update_atom_positions(frame_idx)
|
||||
if bond_lines is not None and len(BOND_PAIRS) > 0:
|
||||
pos = np.zeros((len(BOND_PAIRS) * 2, 3), dtype=np.float32)
|
||||
for b_idx, (i, j) in enumerate(BOND_PAIRS):
|
||||
pos[b_idx * 2] = [DISP_ALL_X[frame_idx, i], DISP_ALL_Y[frame_idx, i], DISP_ALL_Z[frame_idx, i]]
|
||||
pos[b_idx * 2 + 1] = [DISP_ALL_X[frame_idx, j], DISP_ALL_Y[frame_idx, j], DISP_ALL_Z[frame_idx, j]]
|
||||
bond_lines.set_data(pos=pos)
|
||||
_update_bond_positions(frame_idx)
|
||||
# 复位相机
|
||||
view.camera.distance = initial_camera["distance"]
|
||||
view.camera.elevation = initial_camera["elevation"]
|
||||
@@ -413,31 +428,51 @@ def handle_mouse_press(event):
|
||||
axis.visible = axes_visible
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# 位置/键线更新辅助函数(两种渲染模式共用)
|
||||
# ===========================================================================
|
||||
|
||||
def _update_atom_positions(f_idx):
|
||||
"""更新所有原子到第 f_idx 帧的位置。"""
|
||||
if USE_MARKER:
|
||||
for i in range(N_ATOMS):
|
||||
marker_pos[i] = [DISP_ALL_X[f_idx, i], DISP_ALL_Y[f_idx, i], DISP_ALL_Z[f_idx, i]]
|
||||
balls.set_data(pos=marker_pos)
|
||||
else:
|
||||
for i in range(N_ATOMS):
|
||||
balls[i].transform = STTransform(translate=(
|
||||
float(DISP_ALL_X[f_idx, i]),
|
||||
float(DISP_ALL_Y[f_idx, i]),
|
||||
float(DISP_ALL_Z[f_idx, i]),
|
||||
))
|
||||
|
||||
|
||||
def _update_bond_positions(f_idx):
|
||||
"""原地更新键线顶点位置,避免重新分配内存。"""
|
||||
for b_idx, (i, j) in enumerate(BOND_PAIRS):
|
||||
b2 = b_idx * 2
|
||||
bond_pos_buffer[b2] = [DISP_ALL_X[f_idx, i], DISP_ALL_Y[f_idx, i], DISP_ALL_Z[f_idx, i]]
|
||||
bond_pos_buffer[b2 + 1] = [DISP_ALL_X[f_idx, j], DISP_ALL_Y[f_idx, j], DISP_ALL_Z[f_idx, j]]
|
||||
bond_lines.set_data(pos=bond_pos_buffer)
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# 动画初始化
|
||||
# ===========================================================================
|
||||
frame_idx = 0
|
||||
|
||||
# 初始帧:摆放所有小球并刷新 UI
|
||||
for i in range(N_ATOMS):
|
||||
balls[i].transform = STTransform(translate=(
|
||||
float(DISP_ALL_X[frame_idx, i]),
|
||||
float(DISP_ALL_Y[frame_idx, i]),
|
||||
float(DISP_ALL_Z[frame_idx, i]),
|
||||
))
|
||||
# 初始帧:更新成键线
|
||||
# 初始帧:摆放所有原子并刷新 UI
|
||||
_update_atom_positions(frame_idx)
|
||||
if bond_lines is not None and len(BOND_PAIRS) > 0:
|
||||
pos = np.zeros((len(BOND_PAIRS) * 2, 3), dtype=np.float32)
|
||||
for b_idx, (i, j) in enumerate(BOND_PAIRS):
|
||||
pos[b_idx * 2] = [DISP_ALL_X[frame_idx, i], DISP_ALL_Y[frame_idx, i], DISP_ALL_Z[frame_idx, i]]
|
||||
pos[b_idx * 2 + 1] = [DISP_ALL_X[frame_idx, j], DISP_ALL_Y[frame_idx, j], DISP_ALL_Z[frame_idx, j]]
|
||||
bond_lines.set_data(pos=pos)
|
||||
_update_bond_positions(frame_idx)
|
||||
reposition_camera_info()
|
||||
update_ball_info(frame_idx,
|
||||
float(DISP_X[frame_idx]), float(DISP_Y[frame_idx]), float(DISP_Z[frame_idx]),
|
||||
float(DISP_VX[frame_idx]), float(DISP_VY[frame_idx]), float(DISP_VZ[frame_idx]))
|
||||
|
||||
mode_str = "Marker (GPU 实例化)" if USE_MARKER else "Sphere (独立网格)"
|
||||
print(f"[draw] 加载 output/display.txt: {N_FRAMES} 帧, {N_ATOMS} 个原子, NT={NT}, DT={DT}, NSTEP={NSTEP}")
|
||||
print(f"[draw] 渲染方式: {mode_str}")
|
||||
print(f"[draw] 绘图参数: ball_radius={ball_radius}, box_color=({box_color_r:.2f},{box_color_g:.2f},{box_color_b:.2f}), alpha={alpha_list}")
|
||||
|
||||
|
||||
@@ -448,20 +483,12 @@ def update(event):
|
||||
global frame_idx
|
||||
frame_idx = (frame_idx + 1) % N_FRAMES # 循环播放
|
||||
|
||||
# 更新所有小球位置
|
||||
for i in range(N_ATOMS):
|
||||
x = float(DISP_ALL_X[frame_idx, i])
|
||||
y = float(DISP_ALL_Y[frame_idx, i])
|
||||
z = float(DISP_ALL_Z[frame_idx, i])
|
||||
balls[i].transform = STTransform(translate=(x, y, z))
|
||||
# 更新所有原子位置(两种模式共用逻辑)
|
||||
_update_atom_positions(frame_idx)
|
||||
|
||||
# 更新成键线
|
||||
# 更新成键线(原地修改,无内存分配)
|
||||
if bond_lines is not None and len(BOND_PAIRS) > 0:
|
||||
pos = np.zeros((len(BOND_PAIRS) * 2, 3), dtype=np.float32)
|
||||
for b_idx, (i, j) in enumerate(BOND_PAIRS):
|
||||
pos[b_idx * 2] = [DISP_ALL_X[frame_idx, i], DISP_ALL_Y[frame_idx, i], DISP_ALL_Z[frame_idx, i]]
|
||||
pos[b_idx * 2 + 1] = [DISP_ALL_X[frame_idx, j], DISP_ALL_Y[frame_idx, j], DISP_ALL_Z[frame_idx, j]]
|
||||
bond_lines.set_data(pos=pos)
|
||||
_update_bond_positions(frame_idx)
|
||||
|
||||
# 信息面板显示 plot_atom 的数据
|
||||
x = float(DISP_X[frame_idx])
|
||||
|
||||
Reference in New Issue
Block a user