|
|
@@ -0,0 +1,72 @@
|
|
|
+"""GLB 部件重命名:按查看器标签定位节点,写回 .glb,并同步故障↔部件映射表。
|
|
|
+
|
|
|
+- 标签规则与前端一致(three.js createUniqueName:同名按出现次序 ``_1``、``_2``…),
|
|
|
+ 直接复用 ``tools/remove_nodes.viewer_labels``。
|
|
|
+- 名称强制全局唯一(不得与其它任何节点的原始名重复),避免产生后缀歧义。
|
|
|
+- 改名会导致其后同名部件的标签整体前移,故同时生成 旧label→新label 映射,
|
|
|
+ 并调用 ``fault_map_service.remap_parts`` 同步 ``data/fault_part_map.csv``。
|
|
|
+"""
|
|
|
+
|
|
|
+import os
|
|
|
+import sys
|
|
|
+import threading
|
|
|
+
|
|
|
+from config import MODULE_DIR
|
|
|
+from services import fault_map_service, model_service
|
|
|
+
|
|
|
+_TOOLS_DIR = MODULE_DIR / "tools"
|
|
|
+if str(_TOOLS_DIR) not in sys.path:
|
|
|
+ sys.path.insert(0, str(_TOOLS_DIR))
|
|
|
+
|
|
|
+from remove_nodes import viewer_labels # noqa: E402
|
|
|
+from split_glb import read_glb, write_glb # noqa: E402
|
|
|
+
|
|
|
+_lock = threading.Lock()
|
|
|
+
|
|
|
+
|
|
|
+def rename_part(model: str, label: str, new_name: str) -> dict:
|
|
|
+ new_name = (new_name or "").strip()
|
|
|
+ if not new_name:
|
|
|
+ raise ValueError("名称不能为空")
|
|
|
+ if "#" in new_name:
|
|
|
+ raise ValueError("名称不能包含 #")
|
|
|
+ path = model_service.resolve_model(model)
|
|
|
+ if path is None:
|
|
|
+ raise FileNotFoundError("模型不存在")
|
|
|
+ if path.suffix.lower() != ".glb":
|
|
|
+ raise ValueError("仅支持 .glb 模型")
|
|
|
+
|
|
|
+ with _lock:
|
|
|
+ gltf, bin_data = read_glb(str(path))
|
|
|
+ old_labels = viewer_labels(gltf)
|
|
|
+ targets = [i for i, value in old_labels.items() if value == label]
|
|
|
+ if len(targets) != 1:
|
|
|
+ raise LookupError(f"部件标签未唯一匹配:{label}")
|
|
|
+ index = targets[0]
|
|
|
+
|
|
|
+ for j, node in enumerate(gltf["nodes"]):
|
|
|
+ if j == index:
|
|
|
+ continue
|
|
|
+ if (node.get("name") or "") == new_name:
|
|
|
+ raise ValueError(f"名称已存在:{new_name}")
|
|
|
+
|
|
|
+ gltf["nodes"][index]["name"] = new_name
|
|
|
+ new_labels = viewer_labels(gltf)
|
|
|
+ mapping = {
|
|
|
+ old_labels[i]: new_labels[i]
|
|
|
+ for i in old_labels
|
|
|
+ if old_labels[i] != new_labels[i]
|
|
|
+ }
|
|
|
+
|
|
|
+ tmp = str(path) + ".tmp"
|
|
|
+ write_glb(tmp, gltf, bin_data)
|
|
|
+ os.replace(tmp, path)
|
|
|
+
|
|
|
+ remapped = fault_map_service.remap_parts(model, mapping)
|
|
|
+ return {
|
|
|
+ "node_index": index,
|
|
|
+ "old_label": label,
|
|
|
+ "new_label": new_labels[index],
|
|
|
+ "mapping": mapping,
|
|
|
+ "remapped_rows": remapped,
|
|
|
+ }
|