268 lines
7.7 KiB
Python
268 lines
7.7 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
STP到GLB转换工具 - Python包装器
|
||
调用STP2GLB.exe的友好命令行接口
|
||
"""
|
||
|
||
import argparse
|
||
import os
|
||
import sys
|
||
import subprocess
|
||
import pathlib
|
||
from typing import List, Optional, Dict, Any
|
||
|
||
|
||
# 质量预设配置
|
||
QUALITY_PRESETS = {
|
||
'low': {'linear': 0.5, 'angular': 0.8},
|
||
'medium': {'linear': 0.1, 'angular': 0.5}, # 默认值
|
||
'high': {'linear': 0.01, 'angular': 0.1},
|
||
'ultra': {'linear': 0.001, 'angular': 0.05},
|
||
'custom': None # 使用用户指定值
|
||
}
|
||
|
||
|
||
def find_stp2glb_executable() -> Optional[str]:
|
||
"""查找STP2GLB.exe可执行文件"""
|
||
|
||
# 1. 检查环境变量
|
||
if 'STP2GLB_PATH' in os.environ:
|
||
exe_path = os.environ['STP2GLB_PATH']
|
||
if os.path.isfile(exe_path):
|
||
return exe_path
|
||
|
||
# 2. 当前目录
|
||
current_dir = pathlib.Path.cwd()
|
||
exe_candidates = ['STP2GLB.exe', 'STP2GLB']
|
||
|
||
for exe_name in exe_candidates:
|
||
exe_path = current_dir / exe_name
|
||
if exe_path.is_file():
|
||
return str(exe_path)
|
||
|
||
# 3. Pixi环境目录
|
||
pixi_dirs = [
|
||
current_dir / '.pixi' / 'envs' / 'dynamic' / 'Library' / 'bin',
|
||
current_dir / '.pixi' / 'envs' / 'dynamic-debug' / 'Library' / 'bin',
|
||
current_dir / '.pixi' / 'envs' / 'static' / 'Library' / 'bin',
|
||
]
|
||
|
||
for pixi_dir in pixi_dirs:
|
||
for exe_name in exe_candidates:
|
||
exe_path = pixi_dir / exe_name
|
||
if exe_path.is_file():
|
||
return str(exe_path)
|
||
|
||
# 4. 构建目录
|
||
build_dirs = list(current_dir.glob('build/*/'))
|
||
for build_dir in build_dirs:
|
||
for exe_name in exe_candidates:
|
||
exe_path = build_dir / exe_name
|
||
if exe_path.is_file():
|
||
return str(exe_path)
|
||
|
||
return None
|
||
|
||
|
||
def validate_input_file(input_path: str) -> bool:
|
||
"""验证输入文件"""
|
||
if not os.path.isfile(input_path):
|
||
print(f"错误: 输入文件不存在: {input_path}")
|
||
return False
|
||
|
||
# 检查文件扩展名
|
||
ext = pathlib.Path(input_path).suffix.lower()
|
||
if ext not in ['.stp', '.step']:
|
||
print(f"错误: 输入文件必须是.stp或.step格式,当前: {ext}")
|
||
return False
|
||
|
||
return True
|
||
|
||
|
||
def validate_output_file(output_path: str) -> bool:
|
||
"""验证输出文件"""
|
||
# 检查文件扩展名
|
||
ext = pathlib.Path(output_path).suffix.lower()
|
||
if ext != '.glb':
|
||
print(f"错误: 输出文件必须是.glb格式,当前: {ext}")
|
||
return False
|
||
|
||
# 检查目录是否存在
|
||
output_dir = pathlib.Path(output_path).parent
|
||
if not output_dir.exists():
|
||
print(f"错误: 输出目录不存在: {output_dir}")
|
||
return False
|
||
|
||
# 警告如果文件已存在
|
||
if os.path.exists(output_path):
|
||
print(f"警告: 输出文件已存在,将被覆盖: {output_path}")
|
||
|
||
return True
|
||
|
||
|
||
def build_command_args(args: argparse.Namespace, exe_path: str) -> List[str]:
|
||
"""构建命令行参数"""
|
||
cmd_args = [exe_path]
|
||
|
||
# 必需参数
|
||
cmd_args.extend(['--stp', args.input])
|
||
cmd_args.extend(['--glb', args.output])
|
||
|
||
# 质量预设或自定义偏差值
|
||
if args.quality == 'custom':
|
||
if args.linear_deflection is not None:
|
||
cmd_args.extend(['--lin-defl', str(args.linear_deflection)])
|
||
if args.angular_deflection is not None:
|
||
cmd_args.extend(['--ang-defl', str(args.angular_deflection)])
|
||
else:
|
||
preset = QUALITY_PRESETS[args.quality]
|
||
cmd_args.extend(['--lin-defl', str(preset['linear'])])
|
||
cmd_args.extend(['--ang-defl', str(preset['angular'])])
|
||
|
||
# 可选标志
|
||
if args.debug:
|
||
cmd_args.append('--debug')
|
||
|
||
if args.solid_only:
|
||
cmd_args.append('--solid-only')
|
||
|
||
return cmd_args
|
||
|
||
|
||
def run_conversion(cmd_args: List[str]) -> int:
|
||
"""运行转换程序"""
|
||
try:
|
||
print("开始转换...")
|
||
print(f"执行命令: {' '.join(cmd_args)}")
|
||
print("-" * 50)
|
||
|
||
# 运行命令并实时显示输出
|
||
process = subprocess.run(
|
||
cmd_args,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.STDOUT,
|
||
text=True,
|
||
encoding='utf-8'
|
||
)
|
||
|
||
# 显示输出
|
||
if process.stdout:
|
||
print(process.stdout)
|
||
|
||
if process.returncode == 0:
|
||
print("-" * 50)
|
||
print("转换完成!")
|
||
else:
|
||
print("-" * 50)
|
||
print(f"转换失败,退出码: {process.returncode}")
|
||
|
||
return process.returncode
|
||
|
||
except Exception as e:
|
||
print(f"执行错误: {e}")
|
||
return 1
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
parser = argparse.ArgumentParser(
|
||
description='STP到GLB转换工具 - 默认使用层级转换并保留原始组件名称',
|
||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||
)
|
||
|
||
# 位置参数
|
||
parser.add_argument('input', help='输入的STP文件路径')
|
||
parser.add_argument('output', help='输出的GLB文件路径')
|
||
|
||
# 可选参数
|
||
parser.add_argument(
|
||
'--linear-deflection',
|
||
type=float,
|
||
help='线性偏差 (仅在quality=custom时生效)'
|
||
)
|
||
parser.add_argument(
|
||
'--angular-deflection',
|
||
type=float,
|
||
help='角度偏差 (仅在quality=custom时生效)'
|
||
)
|
||
parser.add_argument(
|
||
'--quality',
|
||
choices=['low', 'medium', 'high', 'ultra', 'custom'],
|
||
default='medium',
|
||
help='网格质量等级 (默认: medium)'
|
||
)
|
||
parser.add_argument(
|
||
'--debug',
|
||
action='store_true',
|
||
help='调试模式,提供详细的转换信息'
|
||
)
|
||
parser.add_argument(
|
||
'--solid-only',
|
||
action='store_true',
|
||
help='仅处理实体几何'
|
||
)
|
||
parser.add_argument(
|
||
'--no-scale',
|
||
action='store_true',
|
||
help='禁用自动缩放 (预留功能)'
|
||
)
|
||
parser.add_argument(
|
||
'--no-center',
|
||
action='store_true',
|
||
help='禁用自动居中 (预留功能)'
|
||
)
|
||
|
||
args = parser.parse_args()
|
||
|
||
# 验证自定义质量参数
|
||
if args.quality == 'custom':
|
||
if args.linear_deflection is None and args.angular_deflection is None:
|
||
print("错误: 使用custom质量等级时,必须指定--linear-deflection或--angular-deflection")
|
||
return 1
|
||
|
||
# 验证输入输出文件
|
||
if not validate_input_file(args.input):
|
||
return 1
|
||
|
||
if not validate_output_file(args.output):
|
||
return 1
|
||
|
||
# 查找可执行文件
|
||
exe_path = find_stp2glb_executable()
|
||
if not exe_path:
|
||
print("错误: 未找到STP2GLB.exe可执行文件")
|
||
print("请确保:")
|
||
print("1. 已编译项目 (pixi run build && pixi run install)")
|
||
print("2. 或设置环境变量STP2GLB_PATH指向可执行文件")
|
||
return 1
|
||
|
||
print(f"使用可执行文件: {exe_path}")
|
||
|
||
# 显示质量预设信息
|
||
if args.quality != 'custom':
|
||
preset = QUALITY_PRESETS[args.quality]
|
||
print(f"质量等级: {args.quality}")
|
||
print(f"线性偏差: {preset['linear']}, 角度偏差: {preset['angular']}")
|
||
else:
|
||
print("质量等级: custom")
|
||
if args.linear_deflection is not None:
|
||
print(f"线性偏差: {args.linear_deflection}")
|
||
if args.angular_deflection is not None:
|
||
print(f"角度偏差: {args.angular_deflection}")
|
||
|
||
# 预留功能提示
|
||
if args.no_scale:
|
||
print("注意: --no-scale 功能暂未实现")
|
||
if args.no_center:
|
||
print("注意: --no-center 功能暂未实现")
|
||
|
||
# 构建命令参数
|
||
cmd_args = build_command_args(args, exe_path)
|
||
|
||
# 运行转换
|
||
return run_conversion(cmd_args)
|
||
|
||
|
||
if __name__ == '__main__':
|
||
sys.exit(main())
|