VWED_server/services/scene_to_smap_converter.py

403 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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)