- 🚀 新增FastAPI Web服务支持 - ⚡ 实现异步任务处理和并发转换 - 📊 添加实时进度追踪(0-100%) - 🏗️ 重构为模块化架构:core/api/services/utils - 🔧 完整的任务管理系统和状态追踪 - 📖 自动生成API文档(Swagger/ReDoc) - 🔄 保持CLI模式100%向后兼容 - 🛡️ 增强错误处理和文件验证 - 📝 更新完整文档(README/CLAUDE.md) 技术栈: FastAPI + uvicorn + pydantic + asyncio API端点: /health, /api/v1/convert, /api/v1/status, /api/v1/tasks 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
98 lines
3.6 KiB
Python
98 lines
3.6 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
STP到GLB转换器类
|
||
封装原有转换逻辑,支持进度回调
|
||
"""
|
||
|
||
import os
|
||
import trimesh
|
||
from typing import Callable, Optional
|
||
from OCC.Extend.DataExchange import read_step_file, write_stl_file
|
||
|
||
|
||
class StpToGlbConverter:
|
||
"""STP到GLB转换器"""
|
||
|
||
def __init__(self):
|
||
self.progress_callback: Optional[Callable[[int, str], None]] = None
|
||
|
||
def set_progress_callback(self, callback: Callable[[int, str], None]) -> None:
|
||
"""设置进度回调函数"""
|
||
self.progress_callback = callback
|
||
|
||
def _update_progress(self, progress: int, message: str) -> None:
|
||
"""更新进度"""
|
||
if self.progress_callback:
|
||
self.progress_callback(progress, message)
|
||
|
||
def stp_to_stl(self, stp_path: str, stl_path: str) -> None:
|
||
"""pythonocc 读 STP 写二进制 STL"""
|
||
self._update_progress(5, "检查输入文件...")
|
||
|
||
if not os.path.isfile(stp_path):
|
||
raise FileNotFoundError(f"找不到文件:{stp_path}")
|
||
|
||
self._update_progress(20, "读取STP文件...")
|
||
shape = read_step_file(stp_path)
|
||
|
||
if shape is None:
|
||
raise ValueError(f"无法读取STP文件:{stp_path}")
|
||
|
||
self._update_progress(40, "转换为STL格式...")
|
||
write_stl_file(shape, stl_path, mode="binary", linear_deflection=0.01, angular_deflection=0.1)
|
||
|
||
self._update_progress(60, "STL文件生成完成")
|
||
|
||
def stl_to_glb(self, stl_path: str, glb_path: str, auto_scale: bool = True, auto_center: bool = True) -> None:
|
||
"""使用 trimesh 转换 STL 到 GLB,并优化用于Blender"""
|
||
self._update_progress(65, "检查STL文件...")
|
||
|
||
if not os.path.isfile(stl_path):
|
||
raise FileNotFoundError(f"找不到STL文件:{stl_path}")
|
||
|
||
self._update_progress(70, "加载STL网格...")
|
||
mesh = trimesh.load(stl_path)
|
||
|
||
if mesh.is_empty:
|
||
raise ValueError(f"STL文件为空或无效:{stl_path}")
|
||
|
||
if len(mesh.vertices) == 0 or len(mesh.faces) == 0:
|
||
raise ValueError("网格数据无效")
|
||
|
||
self._update_progress(80, "优化网格...")
|
||
|
||
# 自动缩放和居中
|
||
if auto_scale or auto_center:
|
||
bounds = mesh.bounds
|
||
max_dimension = max(bounds[1] - bounds[0])
|
||
|
||
if auto_scale and max_dimension > 1000:
|
||
scale_factor = 50.0 / max_dimension
|
||
mesh.apply_scale(scale_factor)
|
||
|
||
if auto_center:
|
||
mesh.apply_translation(-mesh.centroid)
|
||
|
||
self._update_progress(90, "导出GLB文件...")
|
||
|
||
try:
|
||
mesh.export(glb_path)
|
||
if os.path.getsize(glb_path) == 0:
|
||
raise ValueError("生成的GLB文件为空")
|
||
except Exception as e:
|
||
raise ValueError(f"GLB导出失败:{e}")
|
||
|
||
self._update_progress(100, "转换完成")
|
||
|
||
def convert(self, stp_path: str, glb_path: str, auto_scale: bool = True, auto_center: bool = True) -> None:
|
||
"""完整的STP到GLB转换流程"""
|
||
stl_tmp = os.path.splitext(glb_path)[0] + ".tmp.stl"
|
||
|
||
try:
|
||
self._update_progress(0, "开始转换...")
|
||
self.stp_to_stl(stp_path, stl_tmp)
|
||
self.stl_to_glb(stl_tmp, glb_path, auto_scale, auto_center)
|
||
finally:
|
||
# 清理临时文件
|
||
if os.path.isfile(stl_tmp):
|
||
os.remove(stl_tmp) |