diff --git a/src/api/v1/__init__.py b/src/api/v1/__init__.py index 9d07e23..b2c46d8 100644 --- a/src/api/v1/__init__.py +++ b/src/api/v1/__init__.py @@ -1,16 +1,27 @@ from fastapi import APIRouter +import importlib -from api.v1.health_router import router as health_router -from api.v1.upload_router import router as upload_router -from api.v1.task_router import router as task_router -from api.v1.history_router import router as history_router -from api.v1.debug_router import router as debug_router -from api.v1.advanced_router import router as advanced_router +from utils.logger import get_logger + +logger = get_logger(__name__) router = APIRouter() -router.include_router(health_router) -router.include_router(upload_router) -router.include_router(task_router) -router.include_router(history_router) -router.include_router(debug_router) -router.include_router(advanced_router) + +def _safe_include(module_path: str, label: str): + try: + module = importlib.import_module(module_path) + router_obj = getattr(module, "router", None) + if router_obj is None: + raise ValueError("未找到 router 对象") + router.include_router(router_obj) + logger.info(f"{label} 路由加载成功") + except Exception as exc: + logger.warning(f"{label} 路由加载失败,已跳过: {exc}") + + +_safe_include("api.v1.health_router", "健康检查") +_safe_include("api.v1.upload_router", "上传") +_safe_include("api.v1.task_router", "任务") +_safe_include("api.v1.history_router", "历史") +_safe_include("api.v1.debug_router", "调试") +_safe_include("api.v1.advanced_router", "高级") diff --git a/src/api/v1/upload_router.py b/src/api/v1/upload_router.py index 87f1fe6..b0663c6 100644 --- a/src/api/v1/upload_router.py +++ b/src/api/v1/upload_router.py @@ -15,7 +15,6 @@ from sqlalchemy.ext.asyncio import AsyncSession from services.auth_service import get_current_active_user from services.processing_service import processing_service from models.database import User -from api.routes import tasks as legacy_tasks logger = get_logger(__name__) @@ -66,7 +65,6 @@ async def upload_stp( upload_time=str(datetime.now()) ) await redis_task_manager.set_task(task_id, task_info) - legacy_tasks[task_id] = task_info # 后台处理(使用独立数据库会话,避免请求会话关闭问题) background_tasks.add_task( diff --git a/src/core/mesh_generator.py b/src/core/mesh_generator.py index f2f6b86..5111e0b 100644 --- a/src/core/mesh_generator.py +++ b/src/core/mesh_generator.py @@ -67,9 +67,13 @@ class MeshGenerator: face_vertices = [] for i in range(1, nb_nodes + 1): pnt = face_triangulation.Node(i) - # 应用位置变换 - pnt.Transform(trsf) - face_vertices.append([float(pnt.X()), float(pnt.Y()), float(pnt.Z())]) + # 使用变换后的拷贝点,避免潜在的原地变换副作用 + transformed = pnt.Transformed(trsf) + face_vertices.append([ + float(transformed.X()), + float(transformed.Y()), + float(transformed.Z()), + ]) # 提取三角形索引 face_indices = [] diff --git a/src/main.py b/src/main.py index a0e9e1f..4a84c8c 100644 --- a/src/main.py +++ b/src/main.py @@ -127,7 +127,15 @@ try: from api.v1 import router as moldinsight_router except Exception as e: moldinsight_router = None - print(f"[WARN] MoldInsight路由未加载: {e}") + print(f"[WARN] MoldInsight v1路由未加载: {e}") + +if moldinsight_router is None: + try: + from api.routes import router as moldinsight_router + print("[WARN] 已回退到旧版 MoldInsight 路由") + except Exception as fallback_error: + moldinsight_router = None + print(f"[WARN] MoldInsight旧版路由也未加载: {fallback_error}") if moldinsight_router is not None: app.include_router(moldinsight_router, prefix="/api") diff --git a/src/utils/html_generator.py b/src/utils/html_generator.py index d2e6338..501ec93 100644 --- a/src/utils/html_generator.py +++ b/src/utils/html_generator.py @@ -221,9 +221,49 @@ class HTMLGenerator: const cavityData = {json.dumps(cavity_data, indent=2) if cavity_data else 'null'}; const pointcloudData = {json.dumps(pointcloud_data, indent=2) if pointcloud_data else 'null'}; + function toFlatArray(data) {{ + if (!Array.isArray(data)) return []; + if (data.length === 0) return []; + return Array.isArray(data[0]) ? data.flat() : data; + }} + + function normalizePositions(rawPositions, center) {{ + const flat = toFlatArray(rawPositions); + if (!flat.length) return []; + const normalized = []; + for (let i = 0; i + 2 < flat.length; i += 3) {{ + const x = Number(flat[i]); + const y = Number(flat[i + 1]); + const z = Number(flat[i + 2]); + if (Number.isFinite(x) && Number.isFinite(y) && Number.isFinite(z)) {{ + normalized.push( + x - Number(center[0] || 0), + y - Number(center[1] || 0), + z - Number(center[2] || 0) + ); + }} + }} + return normalized; + }} + + function fitCameraToScene() {{ + const sceneBox = new THREE.Box3().setFromObject(scene); + if (sceneBox.isEmpty()) return; + const center = new THREE.Vector3(); + const size = new THREE.Vector3(); + sceneBox.getCenter(center); + sceneBox.getSize(size); + const maxDim = Math.max(size.x, size.y, size.z) || 100; + const distance = maxDim * 2.2; + camera.position.set(center.x + distance, center.y + distance, center.z + distance); + controls.target.copy(center); + controls.update(); + }} + // 根据边界框创建几何体 const bbox = geometryData.bounding_box; if (bbox) {{ + const centerOffset = bbox.center || [0, 0, 0]; const width = bbox.dimensions ? bbox.dimensions[0] : 100; const height = bbox.dimensions ? bbox.dimensions[1] : 100; const depth = bbox.dimensions ? bbox.dimensions[2] : 100; @@ -232,12 +272,12 @@ class HTMLGenerator: if (pointcloudData && pointcloudData.points && pointcloudData.points.length > 0) {{ // 创建点云几何体 const pointGeometry = new THREE.BufferGeometry(); - const positions = new Float32Array(pointcloudData.points.flat()); + const positions = new Float32Array(normalizePositions(pointcloudData.points, centerOffset)); pointGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); // 添加法向量(如果有) if (pointcloudData.normals && pointcloudData.normals.length > 0) {{ - const normals = new Float32Array(pointcloudData.normals.flat()); + const normals = new Float32Array(toFlatArray(pointcloudData.normals)); pointGeometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3)); }} @@ -286,8 +326,8 @@ class HTMLGenerator: if (cavityVerts.length > 0 && cavityFaces.length > 0) {{ const cavityGeometry = new THREE.BufferGeometry(); - const positions = new Float32Array(cavityVerts); - const indices = new Uint32Array(cavityFaces); + const positions = new Float32Array(normalizePositions(cavityVerts, centerOffset)); + const indices = new Uint32Array(toFlatArray(cavityFaces)); cavityGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); cavityGeometry.setIndex(new THREE.BufferAttribute(indices, 1)); cavityGeometry.computeVertexNormals(); @@ -331,8 +371,8 @@ class HTMLGenerator: if (coreVerts.length > 0 && coreFaces.length > 0) {{ const coreGeometry = new THREE.BufferGeometry(); - const positions = new Float32Array(coreVerts); - const indices = new Uint32Array(coreFaces); + const positions = new Float32Array(normalizePositions(coreVerts, centerOffset)); + const indices = new Uint32Array(toFlatArray(coreFaces)); coreGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); coreGeometry.setIndex(new THREE.BufferAttribute(indices, 1)); coreGeometry.computeVertexNormals(); @@ -364,7 +404,7 @@ class HTMLGenerator: }} // 创建分型面(红色平面) - const partingGeometry = new THREE.PlaneGeometry(width * 1.2, depth * 1.2); + const partingGeometry = new THREE.PlaneGeometry(width * 1.2, height * 1.2); const partingMaterial = new THREE.MeshBasicMaterial({{ color: 0xF44336, transparent: true, @@ -373,7 +413,6 @@ class HTMLGenerator: }}); partingMesh = new THREE.Mesh(partingGeometry, partingMaterial); - partingMesh.rotation.x = Math.PI / 2; partingMesh.position.set(0, 0, 0); scene.add(partingMesh); @@ -392,8 +431,8 @@ class HTMLGenerator: // 创建简化型腔/A板(定模,分型面以上)— 备用 function createSimpleCavity(width, height, depth) {{ // A板:分型面(Z中心)以上的上半模 - const halfHeight = height / 2; - const cavityGeometry = new THREE.BoxGeometry(width * 1.2, depth * 1.2, halfHeight + 10); + const halfDepth = depth / 2; + const cavityGeometry = new THREE.BoxGeometry(width * 1.2, height * 1.2, halfDepth + 10); const cavityMaterial = new THREE.MeshPhongMaterial({{ color: 0x2196F3, transparent: true, @@ -403,7 +442,7 @@ class HTMLGenerator: cavityMesh = new THREE.Mesh(cavityGeometry, cavityMaterial); // Z轴开模:A板放在分型面以上 - cavityMesh.position.set(0, 0, halfHeight / 2 + 5); + cavityMesh.position.set(0, 0, halfDepth / 2 + 5); scene.add(cavityMesh); const cavityWireframe = new THREE.WireframeGeometry(cavityGeometry); @@ -418,8 +457,8 @@ class HTMLGenerator: // 创建简化型芯/B板(动模,分型面以下)— 备用 function createSimpleCore(width, height, depth) {{ // B板:分型面(Z中心)以下的下半模 - const halfHeight = height / 2; - const coreGeometry = new THREE.BoxGeometry(width * 1.2, depth * 1.2, halfHeight + 10); + const halfDepth = depth / 2; + const coreGeometry = new THREE.BoxGeometry(width * 1.2, height * 1.2, halfDepth + 10); const coreMaterial = new THREE.MeshPhongMaterial({{ color: 0xFF9800, transparent: true, @@ -429,7 +468,7 @@ class HTMLGenerator: coreMesh = new THREE.Mesh(coreGeometry, coreMaterial); // Z轴开模:B板放在分型面以下 - coreMesh.position.set(0, 0, -halfHeight / 2 - 5); + coreMesh.position.set(0, 0, -halfDepth / 2 - 5); scene.add(coreMesh); const coreWireframe = new THREE.WireframeGeometry(coreGeometry); @@ -441,9 +480,8 @@ class HTMLGenerator: coreMesh.add(coreLine); }} - // 设置相机位置 - camera.position.set(200, 200, 200); - camera.lookAt(0, 0, 0); + // 统一按场景包围盒调整相机,避免模型错位/尺度不一致导致观感混乱 + fitCameraToScene(); // 动画循环 function animate() {{