feat: 运动相机支持 + move_camera.txt 关键帧驱动

input.txt 新增:
  move_camera: 0  # 0=固定视角, 1=按 move_camera.txt 运动

move_camera.txt 格式(4列:帧号 距离 俯仰角 方位角):
  0    40.0   0     0
  100  80.0  -30  180
  200  40.0   0   360

display.txt header 传递 camera_keyframes JSON 数组,
draw.py 按帧时间线性插值驱动相机运动(循环播放)。
This commit is contained in:
2026-06-12 07:52:06 +08:00
parent f1afb7c479
commit 22b94011ee
5 changed files with 115 additions and 3 deletions
+27 -1
View File
@@ -32,6 +32,31 @@ def _fmt_alpha(v):
return str(float(v))
def _load_camera_kf(config, runtime_base):
"""加载 move_camera.txt → JSON 字符串,供 display.txt header 使用。"""
if not int(config.get("move_camera", 0)):
return ""
cam_file = str(config.get("move_camera_file",
os.path.join("input", "move_camera.txt")))
cam_path = cam_file
if not os.path.isabs(cam_file):
cam_path = os.path.join(runtime_base, cam_file)
if not os.path.exists(cam_path):
return ""
import json
kfs = []
with open(cam_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
parts = line.split()
if len(parts) >= 4:
kfs.append([int(parts[0]), float(parts[1]),
float(parts[2]), float(parts[3])])
return json.dumps(kfs) if kfs else ""
def read_optional_index(data, key, default_value):
"""Read an optional integer index from structured txt metadata."""
if key not in data:
@@ -257,7 +282,8 @@ def run_case(config_path, runtime_base, input_dir="input", output_dir="output",
"atom_radii": _fmt_alpha(data.get("atom_radii", [])),
"camera_distance": str(config.get("camera_distance", 40.0)),
"camera_elevation": str(config.get("camera_elevation", 0)),
"camera_azimuth": str(config.get("camera_azimuth", 0))}
"camera_azimuth": str(config.get("camera_azimuth", 0)),
"camera_keyframes": _load_camera_kf(config, str(runtime_base))}
n_frames = len(indices)
compute.save_display_txt(