"""场景位置:data/scene_positions.csv 的读写(含初始种子)。 坐标存“参考系”(Z 不含随模型变化的 dz),前端应用时再叠加 dz。 """ import csv import os from typing import Any from config import DATA_DIR, SCENE_CSV FIELDS = [ "id", "name", "camera_x", "camera_y", "camera_z", "target_x", "target_y", "target_z", ] # 初始种子:俯视 / 角度A(参考系坐标,与前端 VIEW_PRESETS 一致)。 SEED: list[tuple[int, str, list[float], list[float]]] = [ (1, "俯视", [2.0, 22.0, -3.2], [2.0, 0.0, -22.0]), (2, "角度A", [5.4, 16.7, -6.3], [9.4, 0.0, -22.5]), ] def _ensure_file() -> None: if SCENE_CSV.exists(): return DATA_DIR.mkdir(parents=True, exist_ok=True) with SCENE_CSV.open("w", encoding="utf-8", newline="") as handle: writer = csv.writer(handle) writer.writerow(FIELDS) for scene_id, name, camera, target in SEED: writer.writerow([scene_id, name, *camera, *target]) def _to_scene(row: dict[str, str]) -> dict[str, Any]: return { "id": int(row["id"]), "name": row["name"], "camera": [float(row["camera_x"]), float(row["camera_y"]), float(row["camera_z"])], "target": [float(row["target_x"]), float(row["target_y"]), float(row["target_z"])], } def _read() -> list[dict[str, Any]]: _ensure_file() with SCENE_CSV.open("r", encoding="utf-8", newline="") as handle: return [_to_scene(row) for row in csv.DictReader(handle)] def _write(scenes: list[dict[str, Any]]) -> None: DATA_DIR.mkdir(parents=True, exist_ok=True) tmp = SCENE_CSV.parent / (SCENE_CSV.name + ".tmp") with tmp.open("w", encoding="utf-8", newline="") as handle: writer = csv.writer(handle) writer.writerow(FIELDS) for scene in scenes: writer.writerow([scene["id"], scene["name"], *scene["camera"], *scene["target"]]) os.replace(tmp, SCENE_CSV) def list_scenes() -> list[dict[str, Any]]: return _read() def add_scene(name: str, camera: list[float], target: list[float]) -> dict[str, Any]: scenes = _read() scene = { "id": max((s["id"] for s in scenes), default=0) + 1, "name": name, "camera": [float(v) for v in camera], "target": [float(v) for v in target], } scenes.append(scene) _write(scenes) return scene def rename_scene(scene_id: int, name: str) -> dict[str, Any] | None: scenes = _read() for scene in scenes: if scene["id"] == scene_id: scene["name"] = name _write(scenes) return scene return None def delete_scene(scene_id: int) -> bool: scenes = _read() remaining = [scene for scene in scenes if scene["id"] != scene_id] if len(remaining) == len(scenes): return False _write(remaining) return True