VWED_server/services/scene_to_smap_converter.py

403 lines
20 KiB
Python
Raw Normal View History

2025-09-25 10:52:52 +08:00
#!/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 <scene_file> <smap_file> [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)