feat: add display_amp parameter for visual displacement amplification
Supports display_amp: [ax, ay, az] in input.txt. On rendering, each atom's displacement from its frame-0 equilibrium is multiplied by the corresponding factor. Physics is unchanged; only the rendered positions are scaled. Useful for visualizing small-amplitude waves that would otherwise be invisible. Example: display_amp: [1.0, 1.0, 5.0] exaggerates z-direction motion 5x. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,20 @@ N_FRAMES = DISP_ALL_X.shape[0]
|
||||
NT = int(disp_data["n_total_frames"])
|
||||
N_ATOMS = int(disp_data["n_total_particles"])
|
||||
DT = float(h.get("DT", 0.001))
|
||||
|
||||
# 视觉位移放大:display_amp: [ax, ay, az],对偏离第0帧的位移乘以倍数
|
||||
_damp_raw = h.get("display_amp", "")
|
||||
if _damp_raw.strip():
|
||||
import ast as _ast
|
||||
_damp_vals = _ast.literal_eval(_damp_raw.strip())
|
||||
_damp = np.array(_damp_vals, dtype=np.float64)
|
||||
if _damp.shape == (3,) and not np.allclose(_damp, 1.0):
|
||||
_eq_x = DISP_ALL_X[0:1, :] # 第0帧作为平衡位置参考
|
||||
_eq_y = DISP_ALL_Y[0:1, :]
|
||||
_eq_z = DISP_ALL_Z[0:1, :]
|
||||
DISP_ALL_X = _eq_x + (DISP_ALL_X - _eq_x) * _damp[0]
|
||||
DISP_ALL_Y = _eq_y + (DISP_ALL_Y - _eq_y) * _damp[1]
|
||||
DISP_ALL_Z = _eq_z + (DISP_ALL_Z - _eq_z) * _damp[2]
|
||||
NSTEP = int(h.get("NSTEP", 1))
|
||||
DISP_STEP = np.arange(N_FRAMES) * NSTEP
|
||||
DISP_T = DISP_STEP * DT
|
||||
@@ -119,11 +133,31 @@ box_color_b = float(h.get("box_color_b", 0.85))
|
||||
info_margin = 8
|
||||
axis_length = 10.0
|
||||
|
||||
import math as _math_cam
|
||||
|
||||
_cx = float(h.get("camera_center_x", 0.0))
|
||||
_cy = float(h.get("camera_center_y", 0.0))
|
||||
_cz = float(h.get("camera_center_z", 0.0))
|
||||
|
||||
# 若 input.txt 指定了摄像机自身坐标,则由坐标反推 distance/elevation/azimuth
|
||||
if h.get("camera_pos_x") is not None:
|
||||
_px = float(h["camera_pos_x"])
|
||||
_py = float(h["camera_pos_y"])
|
||||
_pz = float(h["camera_pos_z"])
|
||||
_dx, _dy, _dz = _px - _cx, _py - _cy, _pz - _cz
|
||||
_dist = _math_cam.sqrt(_dx*_dx + _dy*_dy + _dz*_dz) or 1.0
|
||||
_elev = _math_cam.degrees(_math_cam.asin(max(-1.0, min(1.0, _dy / _dist))))
|
||||
_azim = _math_cam.degrees(_math_cam.atan2(_dx, _dz))
|
||||
else:
|
||||
_dist = float(h.get("camera_distance", 40.0))
|
||||
_elev = float(h.get("camera_elevation", 0))
|
||||
_azim = float(h.get("camera_azimuth", 0))
|
||||
|
||||
initial_camera = {
|
||||
"distance": float(h.get("camera_distance", 40.0)),
|
||||
"elevation": float(h.get("camera_elevation", 0)),
|
||||
"azimuth": float(h.get("camera_azimuth", 0)),
|
||||
"center": (0, 0, 0),
|
||||
"distance": _dist,
|
||||
"elevation": _elev,
|
||||
"azimuth": _azim,
|
||||
"center": (_cx, _cy, _cz),
|
||||
}
|
||||
|
||||
|
||||
@@ -595,7 +629,9 @@ def _load_move_camera_txt():
|
||||
return segs if segs else None
|
||||
|
||||
# 先试 move_camera.txt 直读,没有则用 display.txt 缓存
|
||||
_CAM_MOTION = _load_move_camera_txt()
|
||||
# header 中 camera_keyframes 为空字符串表示 move_camera=0(开关关闭),跳过文件加载
|
||||
_camera_motion_enabled = bool(h.get("camera_keyframes", ""))
|
||||
_CAM_MOTION = _load_move_camera_txt() if _camera_motion_enabled else None
|
||||
if not _CAM_MOTION:
|
||||
_CAM_MOTION = json.loads(h.get("camera_keyframes", "null")) if h.get("camera_keyframes") else None
|
||||
if _CAM_MOTION:
|
||||
@@ -609,14 +645,14 @@ if _CAM_MOTION:
|
||||
|
||||
def _update_motion_camera(f_idx):
|
||||
"""速度段驱动:每帧累加平移/旋转。
|
||||
|
||||
|
||||
时间交叠时所有段同时生效,按文件中出现的顺序依次作用。
|
||||
矩阵操作不具有对易性,排在前面的段优先作用于相机位置。
|
||||
只有当前帧存在活动段时才覆写相机,否则保留用户键盘/鼠标操作的结果。
|
||||
"""
|
||||
if not _CAM_MOTION:
|
||||
return
|
||||
global _cam_center, _cam_elev, _cam_azim, _cam_dist
|
||||
# 找当前帧所有活动的段(时间交叠=同时作用),按文件顺序依次应用
|
||||
active = False
|
||||
for seg in _CAM_MOTION:
|
||||
if seg["start"] <= f_idx < seg["end"]:
|
||||
_cam_center[0] += seg["v"][0]
|
||||
@@ -624,12 +660,13 @@ def _update_motion_camera(f_idx):
|
||||
_cam_center[2] += seg["v"][2]
|
||||
_cam_elev += seg["r"][0]
|
||||
_cam_azim += seg["r"][1]
|
||||
# rz 预留
|
||||
active = True
|
||||
|
||||
view.camera.center = tuple(_cam_center)
|
||||
view.camera.distance = _cam_dist
|
||||
view.camera.elevation = _cam_elev
|
||||
view.camera.azimuth = _cam_azim
|
||||
if active:
|
||||
view.camera.center = tuple(_cam_center)
|
||||
view.camera.distance = _cam_dist
|
||||
view.camera.elevation = _cam_elev
|
||||
view.camera.azimuth = _cam_azim
|
||||
|
||||
|
||||
def update(event):
|
||||
|
||||
Reference in New Issue
Block a user