model_edit_service.py 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. """GLB 部件重命名:按查看器标签定位节点,写回 .glb,并同步故障↔部件映射表。
  2. - 标签规则与前端一致(three.js createUniqueName:同名按出现次序 ``_1``、``_2``…),
  3. 直接复用 ``tools/remove_nodes.viewer_labels``。
  4. - 名称强制全局唯一(不得与其它任何节点的原始名重复),避免产生后缀歧义。
  5. - 改名会导致其后同名部件的标签整体前移,故同时生成 旧label→新label 映射,
  6. 并调用 ``fault_map_service.remap_parts`` 同步 ``data/fault_part_map.csv``。
  7. """
  8. import os
  9. import sys
  10. import threading
  11. from config import MODULE_DIR
  12. from services import fault_map_service, model_service
  13. _TOOLS_DIR = MODULE_DIR / "tools"
  14. if str(_TOOLS_DIR) not in sys.path:
  15. sys.path.insert(0, str(_TOOLS_DIR))
  16. from remove_nodes import viewer_labels # noqa: E402
  17. from split_glb import read_glb, write_glb # noqa: E402
  18. _lock = threading.Lock()
  19. def rename_part(model: str, label: str, new_name: str) -> dict:
  20. new_name = (new_name or "").strip()
  21. if not new_name:
  22. raise ValueError("名称不能为空")
  23. if "#" in new_name:
  24. raise ValueError("名称不能包含 #")
  25. path = model_service.resolve_model(model)
  26. if path is None:
  27. raise FileNotFoundError("模型不存在")
  28. if path.suffix.lower() != ".glb":
  29. raise ValueError("仅支持 .glb 模型")
  30. with _lock:
  31. gltf, bin_data = read_glb(str(path))
  32. old_labels = viewer_labels(gltf)
  33. targets = [i for i, value in old_labels.items() if value == label]
  34. if len(targets) != 1:
  35. raise LookupError(f"部件标签未唯一匹配:{label}")
  36. index = targets[0]
  37. for j, node in enumerate(gltf["nodes"]):
  38. if j == index:
  39. continue
  40. if (node.get("name") or "") == new_name:
  41. raise ValueError(f"名称已存在:{new_name}")
  42. gltf["nodes"][index]["name"] = new_name
  43. new_labels = viewer_labels(gltf)
  44. mapping = {
  45. old_labels[i]: new_labels[i]
  46. for i in old_labels
  47. if old_labels[i] != new_labels[i]
  48. }
  49. tmp = str(path) + ".tmp"
  50. write_glb(tmp, gltf, bin_data)
  51. os.replace(tmp, path)
  52. remapped = fault_map_service.remap_parts(model, mapping)
  53. return {
  54. "node_index": index,
  55. "old_label": label,
  56. "new_label": new_labels[index],
  57. "mapping": mapping,
  58. "remapped_rows": remapped,
  59. }