#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Scene地图到SMAP地图转换器 根据scene地图中的points信息,更新smap地图中对应的坐标位置 """ import json import os from typing import Dict, List, Optional, Tuple class SceneToSmapConverter: """Scene到SMAP转换器""" def __init__(self): self.scene_data = None self.smap_data = None self.point_mapping = {} # scene point name -> scene point data self.route_mapping = {} # scene route (from,to) -> scene route data self.conversion_log = [] # 转换日志 def load_scene_file(self, scene_file_path: str) -> bool: """加载Scene文件""" try: with open(scene_file_path, 'r', encoding='utf-8') as f: self.scene_data = json.load(f) # 建立点位映射 if 'points' in self.scene_data: for point in self.scene_data['points']: if 'name' in point: self.point_mapping[point['name']] = point # 建立路径映射:基于起点和终点名称 if 'routes' in self.scene_data: for route in self.scene_data['routes']: if 'from' in route and 'to' in route: # 先根据ID找到对应的点名称 from_name = None to_name = None for point in self.scene_data.get('points', []): if point.get('id') == route['from']: from_name = point.get('name') if point.get('id') == route['to']: to_name = point.get('name') if from_name and to_name: route_key = f"{from_name}-{to_name}" self.route_mapping[route_key] = route self.conversion_log.append(f"✓ 成功加载Scene文件,共 {len(self.point_mapping)} 个点位,{len(self.route_mapping)} 条路径") return True except Exception as e: self.conversion_log.append(f"✗ 加载Scene文件失败: {str(e)}") return False def load_smap_file(self, smap_file_path: str) -> bool: """加载SMAP文件""" try: with open(smap_file_path, 'r', encoding='utf-8') as f: self.smap_data = json.load(f) self.conversion_log.append("✓ 成功加载SMAP文件") return True except Exception as e: self.conversion_log.append(f"✗ 加载SMAP文件失败: {str(e)}") return False def update_advanced_point_list(self) -> int: """更新SMAP中的advancedPointList""" if not self.smap_data or 'advancedPointList' not in self.smap_data: self.conversion_log.append("✗ SMAP文件中未找到advancedPointList字段") return 0 updated_count = 0 for advanced_point in self.smap_data['advancedPointList']: if 'instanceName' in advanced_point and advanced_point['instanceName'] in self.point_mapping: scene_point = self.point_mapping[advanced_point['instanceName']] # 更新pos坐标 if 'pos' in advanced_point and 'x' in scene_point and 'y' in scene_point: old_pos = f"({advanced_point['pos']['x']}, {advanced_point['pos']['y']})" advanced_point['pos']['x'] = scene_point['x'] advanced_point['pos']['y'] = scene_point['y'] new_pos = f"({scene_point['x']}, {scene_point['y']})" self.conversion_log.append(f" - 更新 {advanced_point['instanceName']}: {old_pos} -> {new_pos}") updated_count += 1 self.conversion_log.append(f"✓ advancedPointList更新完成,共更新 {updated_count} 个点位") return updated_count def update_advanced_curve_list(self) -> int: """更新SMAP中路径的控制点controlPos""" if not self.smap_data or 'advancedCurveList' not in self.smap_data: self.conversion_log.append("✗ SMAP文件中未找到advancedCurveList字段") return 0 updated_count = 0 # 路径类型映射表 path_type_map = { "StraightPath": "line", "BezierPath": "BezierPath", "ArcPath": "bezier2", "DegenerateBezier": "bezier3", "NURBS6": "NURBS6" } for curve in self.smap_data['advancedCurveList']: curve_updated = False # 更新startPos if ('startPos' in curve and 'instanceName' in curve['startPos'] and curve['startPos']['instanceName'] in self.point_mapping): scene_point = self.point_mapping[curve['startPos']['instanceName']] if 'pos' in curve['startPos'] and 'x' in scene_point and 'y' in scene_point: curve['startPos']['pos']['x'] = scene_point['x'] curve['startPos']['pos']['y'] = scene_point['y'] curve_updated = True # 更新endPos if ('endPos' in curve and 'instanceName' in curve['endPos'] and curve['endPos']['instanceName'] in self.point_mapping): scene_point = self.point_mapping[curve['endPos']['instanceName']] if 'pos' in curve['endPos'] and 'x' in scene_point and 'y' in scene_point: curve['endPos']['pos']['x'] = scene_point['x'] curve['endPos']['pos']['y'] = scene_point['y'] curve_updated = True # 根据路径类型更新控制点 if ('startPos' in curve and 'endPos' in curve and 'pos' in curve['startPos'] and 'pos' in curve['endPos']): # 尝试从Scene数据中获取对应的路径信息 start_name = curve['startPos'].get('instanceName', '') end_name = curve['endPos'].get('instanceName', '') route_key = f"{start_name}-{end_name}" scene_route = self.route_mapping.get(route_key) # 获取路径类型 class_name = curve.get('className', '') curve_type = curve.get('type', '') mapped_type = path_type_map.get(class_name, path_type_map.get(curve_type, "bezier3")) start_x = curve['startPos']['pos']['x'] start_y = curve['startPos']['pos']['y'] end_x = curve['endPos']['pos']['x'] end_y = curve['endPos']['pos']['y'] # 根据路径类型更新不同的控制点 if mapped_type == "line": # 直线不需要控制点,但如果存在则保持原值或删除 pass elif mapped_type == "bezier2": # bezier2只需要controlPos1 if 'controlPos1' in curve: if scene_route and 'c1' in scene_route: # 使用Scene中的控制点信息 curve['controlPos1']['x'] = scene_route['c1']['x'] curve['controlPos1']['y'] = scene_route['c1']['y'] else: # 使用中点作为控制点 curve['controlPos1']['x'] = (start_x + end_x) / 2 curve['controlPos1']['y'] = (start_y + end_y) / 2 curve_updated = True elif mapped_type in ["BezierPath", "bezier3"]: # BezierPath和bezier3需要controlPos1和controlPos2 if 'controlPos1' in curve: if scene_route and 'c1' in scene_route: curve['controlPos1']['x'] = scene_route['c1']['x'] curve['controlPos1']['y'] = scene_route['c1']['y'] else: curve['controlPos1']['x'] = start_x + (end_x - start_x) / 3 curve['controlPos1']['y'] = start_y + (end_y - start_y) / 3 curve_updated = True if 'controlPos2' in curve: if scene_route and 'c2' in scene_route: curve['controlPos2']['x'] = scene_route['c2']['x'] curve['controlPos2']['y'] = scene_route['c2']['y'] else: curve['controlPos2']['x'] = start_x + 2 * (end_x - start_x) / 3 curve['controlPos2']['y'] = start_y + 2 * (end_y - start_y) / 3 curve_updated = True elif mapped_type == "NURBS6": # NURBS6需要controlPos1, controlPos2, controlPos3, controlPos4 if 'controlPos1' in curve: if scene_route and 'c1' in scene_route: curve['controlPos1']['x'] = scene_route['c1']['x'] curve['controlPos1']['y'] = scene_route['c1']['y'] if 'z' in scene_route['c1']: curve['controlPos1']['z'] = scene_route['c1']['z'] else: curve['controlPos1']['x'] = start_x + (end_x - start_x) / 5 curve['controlPos1']['y'] = start_y + (end_y - start_y) / 5 if 'z' in curve['controlPos1']: start_z = curve['startPos']['pos'].get('z', 0) end_z = curve['endPos']['pos'].get('z', 0) curve['controlPos1']['z'] = start_z + (end_z - start_z) / 5 curve_updated = True if 'controlPos2' in curve: if scene_route and 'c2' in scene_route: curve['controlPos2']['x'] = scene_route['c2']['x'] curve['controlPos2']['y'] = scene_route['c2']['y'] if 'z' in scene_route['c2']: curve['controlPos2']['z'] = scene_route['c2']['z'] else: curve['controlPos2']['x'] = start_x + 2 * (end_x - start_x) / 5 curve['controlPos2']['y'] = start_y + 2 * (end_y - start_y) / 5 if 'z' in curve['controlPos2']: start_z = curve['startPos']['pos'].get('z', 0) end_z = curve['endPos']['pos'].get('z', 0) curve['controlPos2']['z'] = start_z + 2 * (end_z - start_z) / 5 curve_updated = True if 'controlPos3' in curve: if scene_route and 'c3' in scene_route: curve['controlPos3']['x'] = scene_route['c3']['x'] curve['controlPos3']['y'] = scene_route['c3']['y'] if 'z' in scene_route['c3']: curve['controlPos3']['z'] = scene_route['c3']['z'] else: curve['controlPos3']['x'] = start_x + 3 * (end_x - start_x) / 5 curve['controlPos3']['y'] = start_y + 3 * (end_y - start_y) / 5 if 'z' in curve['controlPos3']: start_z = curve['startPos']['pos'].get('z', 0) end_z = curve['endPos']['pos'].get('z', 0) curve['controlPos3']['z'] = start_z + 3 * (end_z - start_z) / 5 curve_updated = True if 'controlPos4' in curve: if scene_route and 'c4' in scene_route: curve['controlPos4']['x'] = scene_route['c4']['x'] curve['controlPos4']['y'] = scene_route['c4']['y'] if 'z' in scene_route['c4']: curve['controlPos4']['z'] = scene_route['c4']['z'] else: curve['controlPos4']['x'] = start_x + 4 * (end_x - start_x) / 5 curve['controlPos4']['y'] = start_y + 4 * (end_y - start_y) / 5 if 'z' in curve['controlPos4']: start_z = curve['startPos']['pos'].get('z', 0) end_z = curve['endPos']['pos'].get('z', 0) curve['controlPos4']['z'] = start_z + 4 * (end_z - start_z) / 5 curve_updated = True if curve_updated: updated_count += 1 # 统计不同路径类型的数量 type_counts = {} for curve in self.smap_data['advancedCurveList']: class_name = curve.get('className', '') curve_type = curve.get('type', '') mapped_type = path_type_map.get(class_name, path_type_map.get(curve_type, "bezier3")) type_counts[mapped_type] = type_counts.get(mapped_type, 0) + 1 type_info = ", ".join([f"{t}: {c}" for t, c in type_counts.items()]) self.conversion_log.append(f"✓ advancedCurveList更新完成,共更新 {updated_count} 条路径") self.conversion_log.append(f" 路径类型分布: {type_info}") return updated_count def update_bin_locations_list(self) -> int: """更新SMAP中binLocationsList中点位的stringValue参数""" if not self.smap_data or 'binLocationsList' not in self.smap_data: self.conversion_log.append("✗ SMAP文件中未找到binLocationsList字段") return 0 updated_count = 0 for bin_location_group in self.smap_data['binLocationsList']: if 'binLocationList' not in bin_location_group: continue for bin_location in bin_location_group['binLocationList']: if ('pointName' in bin_location and bin_location['pointName'] in self.point_mapping): scene_point = self.point_mapping[bin_location['pointName']] # 更新pos坐标 if 'pos' in bin_location and 'x' in scene_point and 'y' in scene_point: bin_location['pos']['x'] = scene_point['x'] bin_location['pos']['y'] = scene_point['y'] # 更新property中的points字段(stringValue) if 'property' in bin_location: for prop in bin_location['property']: if (prop.get('key') == 'points' and prop.get('type') == 'json' and 'stringValue' in prop): try: # 解析原来的points数据 points_data = json.loads(prop['stringValue']) if isinstance(points_data, list) and len(points_data) > 0: # 计算偏移量(假设第一个点是基准点) if len(points_data) > 0 and 'x' in points_data[0] and 'y' in points_data[0]: # 计算中心点偏移 center_x = sum(p['x'] for p in points_data) / len(points_data) center_y = sum(p['y'] for p in points_data) / len(points_data) offset_x = scene_point['x'] - center_x offset_y = scene_point['y'] - center_y # 更新所有点的坐标 for point in points_data: point['x'] += offset_x point['y'] += offset_y # 更新stringValue prop['stringValue'] = json.dumps(points_data) updated_count += 1 self.conversion_log.append(f" - 更新 {bin_location['pointName']} 的区域坐标") except (json.JSONDecodeError, KeyError, TypeError) as e: self.conversion_log.append(f" - 解析 {bin_location['pointName']} 的points数据失败: {str(e)}") self.conversion_log.append(f"✓ binLocationsList更新完成,共更新 {updated_count} 个点位区域") return updated_count def convert(self, scene_file_path: str, smap_file_path: str, output_path: str = None) -> dict: """执行转换""" self.conversion_log = [] # 加载文件 if not self.load_scene_file(scene_file_path): return {"success": False, "log": self.conversion_log} if not self.load_smap_file(smap_file_path): return {"success": False, "log": self.conversion_log} # 执行转换 total_updated = 0 total_updated += self.update_advanced_point_list() total_updated += self.update_advanced_curve_list() total_updated += self.update_bin_locations_list() # 保存结果 if output_path is None: base_name = os.path.splitext(os.path.basename(smap_file_path))[0] output_path = f"{base_name}_updated.smap" try: with open(output_path, 'w', encoding='utf-8') as f: json.dump(self.smap_data, f, ensure_ascii=False, indent=2) self.conversion_log.append(f"✓ 转换完成,共更新 {total_updated} 项") self.conversion_log.append(f"✓ 输出文件: {output_path}") return { "success": True, "output_file": output_path, "updated_count": total_updated, "log": self.conversion_log, "data": self.smap_data } except Exception as e: self.conversion_log.append(f"✗ 保存文件失败: {str(e)}") return {"success": False, "log": self.conversion_log} def convert_scene_to_smap(scene_file_path: str, smap_file_path: str, output_path: str = None) -> dict: """Scene到SMAP转换的主入口函数""" converter = SceneToSmapConverter() return converter.convert(scene_file_path, smap_file_path, output_path) if __name__ == "__main__": # 测试转换 import sys if len(sys.argv) < 3: print("使用方法: python scene_to_smap_converter.py [output_file]") sys.exit(1) scene_file = sys.argv[1] smap_file = sys.argv[2] output_file = sys.argv[3] if len(sys.argv) > 3 else None result = convert_scene_to_smap(scene_file, smap_file, output_file) print("\n=== 转换结果 ===") for log_entry in result.get('log', []): print(log_entry) if result['success']: print(f"\n转换成功!输出文件: {result.get('output_file', 'N/A')}") else: print("\n转换失败!") sys.exit(1)