尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

3d打印自动生成连接件

3d打印自动生成连接件 目录思路和步骤确定凸、凹部件2. 计算部件质心3. 计算方向向量 dir_vec4. 构建旋转矩阵将默认 Z 轴旋转到 dir_vec5. 应用旋转与平移生成凸起圆柱6. 应用旋转与平移生成孔洞圆柱用于布尔差集7. 布尔操作源代码思路和步骤确定凸、凹部件对于每一对相交的核心部件(i, j)比较它们的体积pythonvol_i abs(world_geoms[i].volume) vol_j abs(world_geoms[j].volume) if vol_i vol_j: convex_idx, concave_idx i, j else: convex_idx, concave_idx j, i体积较大的部件作为凸部件在其上加凸起体积较小的作为凹部件在其上挖孔。注此处“凸/凹”仅用于区分角色方向计算依赖于质心连线。2. 计算部件质心预先为所有核心部件计算质心若无法获取则用顶点均值pythoncentroids[idx] geom.centroid if hasattr(geom, centroid) else np.mean(geom.vertices, axis0)3. 计算方向向量dir_vec取凸部件质心指向凹部件质心的向量pythondir_vec centroids[concave_idx] - centroids[convex_idx]归一化pythonnorm np.linalg.norm(dir_vec) if norm 1e-8: continue # 避免零向量 dir_vec dir_vec / norm该向量即为圆柱的目标轴线方向Z轴最终对齐的方向。4. 构建旋转矩阵将默认 Z 轴旋转到dir_vec默认圆柱体trimesh.creation.cylinder的轴线沿世界坐标系的 Z 轴z_axis [0,0,1]。使用Rodrigues 旋转公式轴角法构造旋转矩阵若dir_vec与z_axis近似平行同向或反向则旋转矩阵取单位阵反向时不需要特殊处理因为后续位置偏移会补偿。否则pythonv np.cross(z_axis, dir_vec) # 旋转轴 s np.linalg.norm(v) c np.dot(z_axis, dir_vec) # 余弦值 vx np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]]) rot np.eye(3) vx np.dot(vx, vx) * ((1 - c) / (s ** 2))得到的rot满足rot z_axis dir_vec即圆柱的轴线将被旋转到dir_vec方向。5. 应用旋转与平移生成凸起圆柱创建默认圆柱中心在原点高度peg_extend沿 Z 轴pythonpeg_cyl trimesh.creation.cylinder(radiuspeg_radius, heightpeg_extend, segments24)计算圆柱的新中心位置让圆柱的底面恰好位于交面中心center并向dir_vec方向伸出即朝凹部件方向pythonpeg_mid center dir_vec * (peg_extend / 2.0)这样圆柱的底部Z负端落在center顶部在center dir_vec * peg_extend恰好从接触面向外伸出。组合旋转和平移变换pythonT_peg np.eye(4) T_peg[:3, :3] rot T_peg[:3, 3] peg_mid peg_cyl.apply_transform(T_peg)此时圆柱轴线沿dir_vec并从接触面伸出。6. 应用旋转与平移生成孔洞圆柱用于布尔差集创建默认圆柱半径稍大带公差pythonhole_cyl trimesh.creation.cylinder(radiushole_radius, heighthole_depth, segments24)计算孔洞圆柱的中心位置为了让圆柱穿过接触面进入凹部件内部将中心向dir_vec方向偏移一个较小的量代码中为hole_depth / 6.0这仅是一个粗略定位确保圆柱体大部分在凹部件内部布尔差集后即可挖出通孔pythonhole_mid center dir_vec * (hole_depth / 6.0)同样应用旋转和平移pythonT_hole np.eye(4) T_hole[:3, :3] rot T_hole[:3, 3] hole_mid hole_cyl.apply_transform(T_hole)圆柱轴线同样沿dir_vec从接触面伸入凹部件内部。7. 布尔操作将旋转后的凸起圆柱与凸部件做并集union得到带凸起的部件。将旋转后的孔洞圆柱与凹部件做差集difference得到带孔的部件。源代码import time import trimesh import numpy as np # -------------------- 辅助函数 -------------------- def prepare_mesh(mesh): 修复网格常见问题返回副本 mesh mesh.copy() mesh.merge_vertices() mesh.fix_normals() if not mesh.is_watertight: mesh mesh.fill_holes() if hasattr(mesh, remove_degenerate_faces): mesh.remove_degenerate_faces() try: mesh.remove_unreferenced_vertices() mesh.remove_infinite_values() except Exception as e: print(e) return mesh def bounds_intersect(a_bounds, b_bounds): 检查两个包围盒是否相交 return not (a_bounds[0][0] b_bounds[1][0] or a_bounds[1][0] b_bounds[0][0] or a_bounds[0][1] b_bounds[1][1] or a_bounds[1][1] b_bounds[0][1] or a_bounds[0][2] b_bounds[1][2] or a_bounds[1][2] b_bounds[0][2]) def is_valid_mesh(geom, check_volumeTrue, min_vertices3, min_faces1): 检查网格是否有效 if not isinstance(geom, trimesh.Trimesh): return False if geom.vertices is None or geom.faces is None: return False if geom.vertices.shape[0] min_vertices: return False if geom.faces.shape[0] min_faces: return False if not np.all(np.isfinite(geom.vertices)): return False if not np.all(np.isfinite(geom.faces)): return False if geom.faces.max() geom.vertices.shape[0]: return False if geom.faces.min() 0: return False try: bounds geom.bounds if not np.all(np.isfinite(bounds)): return False size bounds[1] - bounds[0] if np.any(size 0): return False except: return False if check_volume: try: volume geom.volume if abs(volume) 1e-8: size geom.bounds[1] - geom.bounds[0] if np.all(size 1e-6): pass except: return False try: triangles geom.vertices[geom.faces] v0 triangles[:, 1] - triangles[:, 0] v1 triangles[:, 2] - triangles[:, 0] cross np.cross(v0, v1) areas 0.5 * np.linalg.norm(cross, axis1) if np.mean(areas) 1e-10: return False except: pass return True # -------------------- 核心切割与合并 -------------------- def cut_scene_geometries(scene, enginemanifold, top_k6, peg_radiusNone, peg_lengthNone, add_visual_connectorsFalse): 按体积选择 top_k 个核心部件对其他部件执行切割核心部件被切割 然后将每个未选中的部件合并到与之接触面积最大的核心部件中。 之后在核心部件的接触面上生成凸起和孔洞用于3D打印插接。 返回 (新场景, 交面信息列表) 交面信息为 (name_i, name_j, center, area, normal) if not isinstance(scene, trimesh.Scene): raise ValueError(输入必须是 trimesh.Scene) # 1. 变换到世界坐标系 geom_names [] world_geoms [] for name, geom in scene.geometry.items(): if not isinstance(geom, trimesh.Trimesh): continue if name in scene.graph: transform scene.graph[name][0] else: transform np.eye(4) vertices trimesh.transformations.transform_points(geom.vertices, transform) world_geom trimesh.Trimesh(verticesvertices, facesgeom.faces, processFalse) try: world_geom prepare_mesh(world_geom) print(f预处理 {name} 成功) except Exception as e: print(f预处理几何体 {name} 失败: {e}) geom_names.append(name) world_geoms.append(world_geom) n len(world_geoms) # 2. 计算体积 volumes [] for i, geom in enumerate(world_geoms): try: vol geom.volume if vol 0: vol -vol except: size geom.bounds[1] - geom.bounds[0] vol np.prod(size) volumes.append(vol) print(f部件 {geom_names[i]} 体积: {vol:.6f}) sorted_indices np.argsort(volumes)[::-1] keep_indices set(sorted_indices[:top_k].tolist()) print(f\n选择体积最大的 {top_k} 个部件作为核心: {[geom_names[i] for i in keep_indices]}) # 3. 计算所有交面含法线 all_intersections [] # (i, j, center, area, normal) for i in range(n): for j in range(i 1, n): A world_geoms[i] B world_geoms[j] if not bounds_intersect(A.bounds, B.bounds): continue print(f计算交集: {geom_names[i]} ∩ {geom_names[j]}) try: inter trimesh.boolean.intersection([A, B], engineengine) if inter is not None: if isinstance(inter, list) and len(inter) 0: combined trimesh.util.concatenate(inter) else: combined inter if isinstance(combined, trimesh.Trimesh) and combined.vertices.shape[0] 0 and combined.faces.shape[0] 0: face_centers combined.vertices[combined.faces].mean(axis1) center np.mean(face_centers, axis0) area combined.area face_normals combined.face_normals face_areas combined.area_faces if face_normals.shape[0] 0 and face_areas.sum() 1e-12: weighted_normal np.average(face_normals, axis0, weightsface_areas) norm np.linalg.norm(weighted_normal) normal weighted_normal / norm if norm 1e-12 else np.array([0, 0, 1]) else: normal np.array([0, 0, 1]) all_intersections.append((i, j, center, area, normal)) print(f 记录交面中心: {center}, 面积: {area:.6f}, 法线: {normal}) else: print( 交集为空或无效) else: print( 交集返回 None) except Exception as e: print(f 计算交集失败: {e}) # 4. 执行切割核心部件作为被减数j被其他所有部件切割 for i, j, _, _, _ in all_intersections: if j not in keep_indices: continue A world_geoms[i] B world_geoms[j] print(f执行切割: {geom_names[j]} {geom_names[j]} - {geom_names[i]}) try: result trimesh.boolean.difference([B, A], engineengine) if result is not None: if isinstance(result, list) and len(result) 0: if len(result) 1: vols [r.volume for r in result] result result[np.argmax(vols)] else: result result[0] if isinstance(result, trimesh.Trimesh) and result.vertices.shape[0] 0 and result.faces.shape[0] 0: world_geoms[j] result print(f 成功切割 {geom_names[j]}) else: print(f 切割结果无效保留原始部件) else: print( 切割返回 None保留原始部件) except Exception as e: print(f 切割失败: {e}保留原始部件) # 5. 合并未选中部件到与之接触面积最大的核心部件 unselected_indices [idx for idx in range(n) if idx not in keep_indices] print(f\n未选中的部件索引: {unselected_indices}共 {len(unselected_indices)} 个) contact_map {idx: {} for idx in unselected_indices} for i, j, _, area, _ in all_intersections: if i in keep_indices and j in unselected_indices: contact_map[j][i] contact_map[j].get(i, 0.0) area elif j in keep_indices and i in unselected_indices: contact_map[i][j] contact_map[i].get(j, 0.0) area for un_idx in unselected_indices: if not contact_map[un_idx]: print(f警告: 部件 {geom_names[un_idx]} 与任何核心部件均无接触将保持独立) continue best_core max(contact_map[un_idx], keycontact_map[un_idx].get) best_area contact_map[un_idx][best_core] print(f将 {geom_names[un_idx]} 合并到核心 {geom_names[best_core]} (交面面积 {best_area:.6f})) try: merged trimesh.util.concatenate([world_geoms[best_core], world_geoms[un_idx]]) merged prepare_mesh(merged) if merged.vertices.shape[0] 0 and merged.faces.shape[0] 0: world_geoms[best_core] merged else: print(f 合并结果无效保留独立) except Exception as e: print(f 合并失败: {e}保留独立) # 5.5 在核心部件上生成凸起和孔洞用于3D打印插接 print(\n 开始生成凸起和孔洞 ) core_indices sorted(keep_indices) world_geoms add_peg_and_hole_to_parts( world_geoms, geom_names, all_intersections, core_indices, radiuspeg_radius, lengthpeg_length, engineengine ) # 6. 构建新场景只保留核心部件 new_scene trimesh.Scene() for idx in core_indices: name geom_names[idx] geom world_geoms[idx] if geom.vertices.shape[0] 0 and geom.faces.shape[0] 0: new_scene.add_geometry(geom, geom_namename, transformnp.eye(4)) print(f添加核心部件: {name} (已合并相邻未选中部件并已加工凸起/孔洞)) else: print(f警告: 核心部件 {name} 无效跳过添加) # 可选添加可视化的连接件独立圆柱 if add_visual_connectors: new_scene add_connectors_as_visual(new_scene, all_intersections, core_namesgeom_names) print(f最终场景包含 {len(new_scene.geometry)} 个几何体) print(f几何体名称列表: {list(new_scene.geometry.keys())}) # 7. 返回交面信息含法线 intersections_return [] for i, j, center, area, normal in all_intersections: intersections_return.append((geom_names[i], geom_names[j], center, area, normal)) return new_scene, intersections_return import trimesh import numpy as np def add_peg_and_hole_to_parts(world_geoms, geom_names, intersections, core_indices, radiusNone, lengthNone, tolerance0.002, enginemanifold): 微调版 1. 凸起底面严格贴相交面中心只向外侧凸出去不埋入凸零件 2. 孔洞开口严格贴相交面中心向凹零件内部挖深度按【凹部件包围盒厚度】比例计算 3. peg伸出 hole深度装配不会顶死沿用原有质心dir_vec方向逻辑 all_verts np.vstack([g.vertices for g in world_geoms if g.vertices.shape[0] 0]) scene_diag np.linalg.norm(np.ptp(all_verts, axis0)) if radius is None: peg_radius max(0.01 * scene_diag, 0.001) else: peg_radius radius hole_radius peg_radius tolerance centroids {} for idx in core_indices: g world_geoms[idx] centroids[idx] g.centroid if hasattr(g, centroid) else np.mean(g.vertices, axis0) processed_pairs set() for i, j, center, area, normal in intersections: if i not in core_indices or j not in core_indices: continue pair tuple(sorted((i, j))) if pair in processed_pairs: continue processed_pairs.add(pair) vol_i abs(getattr(world_geoms[i], volume, 0)) vol_j abs(getattr(world_geoms[j], volume, 0)) if vol_i vol_j: convex_idx, concave_idx i, j else: convex_idx, concave_idx j, i geom_convex world_geoms[convex_idx] geom_concave world_geoms[concave_idx] # dir_vec凸部件质心 → 凹部件质心向外就是 -dir_vec dir_vec centroids[concave_idx] - centroids[convex_idx] norm np.linalg.norm(dir_vec) if norm 1e-8: continue dir_vec dir_vec / norm # 按凹部件包围盒计算孔洞深度 bbox_concave geom_concave.bounds bbox_extent bbox_concave[1] - bbox_concave[0] # 凹零件沿dir_vec方向的物理厚度 concave_thickness np.dot(bbox_extent, np.abs(dir_vec)) # 取凹零件厚度的0.25作为孔洞深度下限不小于销直径避免孔过浅 hole_depth max(concave_thickness * 0.15, peg_radius * 1.2) # 凸起伸出长度比孔洞短一点装配留余量 peg_extend hole_depth * 0.85 # 旋转矩阵圆柱Z对齐dir_vec z_axis np.array([0, 0, 1]) if np.allclose(dir_vec, z_axis) or np.allclose(dir_vec, -z_axis): rot np.eye(3) else: v np.cross(z_axis, dir_vec) s np.linalg.norm(v) c np.dot(z_axis, dir_vec) vx np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]]) rot np.eye(3) vx np.dot(vx, vx) * ((1 - c) / (s ** 2)) # 凸起peg底面正好在center朝 -dir_vec凸零件外侧伸出去 # cylinder高度peg_extend圆柱中点 center 向外偏移半高底面落在center peg_cyl trimesh.creation.cylinder(radiuspeg_radius, heightpeg_extend, segments24) # peg_mid center - dir_vec * (peg_extend / 2.0) peg_mid center dir_vec * (peg_extend / 2.0) T_peg np.eye(4) T_peg[:3, :3] rot T_peg[:3, 3] peg_mid peg_cyl.apply_transform(T_peg) # 孔洞hole开口正好在center朝 dir_vec凹零件内部挖入 hole_cyl trimesh.creation.cylinder(radiushole_radius, heighthole_depth, segments24) # hole_mid center dir_vec * (hole_depth / 2.0) hole_mid center dir_vec * (hole_depth / 6.0) T_hole np.eye(4) T_hole[:3, :3] rot T_hole[:3, 3] hole_mid hole_cyl.apply_transform(T_hole) # 布尔合并凸起 try: new_convex trimesh.boolean.union([geom_convex, peg_cyl], engineengine) if isinstance(new_convex, trimesh.Trimesh) and new_convex.is_volume: world_geoms[convex_idx] new_convex print(f✅ 凸起 {geom_names[convex_idx]} 伸出:{peg_extend:.4f}) else: print(f⚠️ 凸起合并失败 {geom_names[convex_idx]}) except Exception as e: print(f❌ 凸起异常 {geom_names[convex_idx]}: {e}) # 布尔挖孔洞 try: new_concave trimesh.boolean.difference([geom_concave, hole_cyl], engineengine) if isinstance(new_concave, trimesh.Trimesh) and new_concave.is_volume: world_geoms[concave_idx] new_concave print(f✅ 孔洞 {geom_names[concave_idx]} 深度:{hole_depth:.4f}) else: print(f⚠️ 孔洞挖除失败 {geom_names[concave_idx]}) except Exception as e: print(f❌ 孔洞异常 {geom_names[concave_idx]}: {e}) return world_geoms def add_connectors_as_visual(scene, intersections, core_namesNone, radiusNone, lengthNone): 添加独立的小圆柱作为连接件指示两端指向两个部件的质心 if core_names is None: core_names list(scene.geometry.keys()) # 计算质心 centroids {} for name in core_names: geom scene.geometry.get(name) if geom and hasattr(geom, centroid): centroids[name] geom.centroid elif geom and hasattr(geom, vertices) and geom.vertices.shape[0] 0: centroids[name] np.mean(geom.vertices, axis0) else: centroids[name] np.array([0, 0, 0]) # 自动尺寸 bounds scene.bounds if bounds is not None and np.all(np.isfinite(bounds)): scene_size np.linalg.norm(bounds[1] - bounds[0]) else: scene_size 1.0 if radius is None: radius max(0.005 * scene_size, 0.001) if length is None: length max(0.015 * scene_size, 0.005) added 0 for item in intersections: if len(item) 5: continue name_i, name_j, center, area, normal item[:5] if name_i not in core_names or name_j not in core_names: continue c_i centroids.get(name_i) c_j centroids.get(name_j) if c_i is None or c_j is None: continue direction c_j - c_i norm_dir np.linalg.norm(direction) if norm_dir 1e-8: continue direction direction / norm_dir cyl trimesh.creation.cylinder(radiusradius, heightlength, segments16) z_axis np.array([0, 0, 1]) if np.allclose(direction, z_axis) or np.allclose(direction, -z_axis): rot np.eye(3) else: v np.cross(z_axis, direction) s np.linalg.norm(v) c np.dot(z_axis, direction) vx np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]]) rot np.eye(3) vx np.dot(vx, vx) * ((1 - c) / (s ** 2)) transform np.eye(4) transform[:3, :3] rot transform[:3, 3] center cyl.apply_transform(transform) conn_name fvisual_connector_{name_i}_{name_j} scene.add_geometry(cyl, geom_nameconn_name, transformnp.eye(4)) added 1 print(f添加可视化连接件: {conn_name} 于 {center}) print(f共添加 {added} 个可视化连接件) return scene # -------------------- 爆炸视图生成 -------------------- def explode_mesh(mesh, intersectionsNone, explosion_scale0.4, area_threshold_ratio0.06): 生成爆炸视图并在部件之间绘制连接线基于交面中心。 若提供了 intersections含法线仍仅使用中心和面积过滤。 if isinstance(mesh, trimesh.Scene): scene mesh elif isinstance(mesh, trimesh.Trimesh): print(Warning: Single mesh provided, cant create exploded view) scene trimesh.Scene(mesh) return scene else: print(fWarning: Unexpected mesh type: {type(mesh)}) scene mesh if len(scene.geometry) 1: print(Only one geometry found - nothing to explode) return scene print(f[EXPLODE_MESH] Starting mesh explosion with scale {explosion_scale}) print(f[EXPLODE_MESH] Processing {len(scene.geometry)} parts) exploded_scene trimesh.Scene() part_centers [] geometry_names [] for geometry_name, geometry in scene.geometry.items(): if hasattr(geometry, vertices) and geometry.vertices.shape[0] 0: center np.mean(geometry.vertices, axis0) part_centers.append(center) geometry_names.append(geometry_name) print(f[EXPLODE_MESH] Part {geometry_name}: center {center}) if not part_centers: print(No valid geometries with vertices found) return scene part_centers np.array(part_centers) global_center np.mean(part_centers, axis0) print(f[EXPLODE_MESH] Global center: {global_center}) offsets {} for i, (geometry_name, geometry) in enumerate(scene.geometry.items()): if hasattr(geometry, vertices) and geometry.vertices.shape[0] 0: if i len(part_centers): part_center part_centers[i] direction part_center - global_center direction_norm np.linalg.norm(direction) if direction_norm 1e-6: direction direction / direction_norm else: direction np.random.randn(3) direction direction / np.linalg.norm(direction) offset direction * explosion_scale offsets[geometry_name] offset else: offset np.zeros(3) offsets[geometry_name] offset transform np.eye(4) transform[:3, 3] offset exploded_scene.add_geometry(geometry, transformtransform, geom_namegeometry_name) print(f[EXPLODE_MESH] Part {geometry_name}: moved by {np.linalg.norm(offset):.4f}) # 添加连接线基于交面中心 if intersections is not None and len(intersections) 0: areas [item[3] for item in intersections if len(item) 4] if areas: max_area max(areas) threshold max_area * area_threshold_ratio print(f[EXPLODE_MESH] 最大交面面积: {max_area:.6f}, 阈值({threshold:.6f})将保留连接线) else: max_area None threshold None all_points [] line_indices [] filtered_count 0 for item in intersections: if len(item) 3: name_i, name_j, center item[0], item[1], item[2] else: continue if max_area is not None and len(item) 4: area item[3] if area threshold: filtered_count 1 print(f[EXPLODE_MESH] 忽略小面积交面: {name_i} ∩ {name_j} (面积{area:.6f})) continue if name_i in offsets and name_j in offsets: p1 center offsets[name_i] p2 center offsets[name_j] idx1 len(all_points) all_points.append(p1) idx2 len(all_points) all_points.append(p2) line_indices.append([idx1, idx2]) print(f[EXPLODE_MESH] Line between {name_i} and {name_j}) else: print(f[EXPLODE_MESH] 跳过连线 {name_i} ↔ {name_j}部件不存在或已合并) if filtered_count 0: print(f[EXPLODE_MESH] 共过滤掉 {filtered_count} 个小面积交面) if line_indices: vertices np.array(all_points) entities [] for idx_pair in line_indices: entities.append(trimesh.path.entities.Line(pointsnp.array(idx_pair))) path trimesh.path.Path3D(entitiesentities, verticesvertices) exploded_scene.add_geometry(path, geom_nameconnection_lines, transformnp.eye(4)) print(f[EXPLODE_MESH] Added {len(line_indices)} connection lines) else: print([EXPLODE_MESH] No connection lines to add (all filtered out or none)) print([EXPLODE_MESH] Mesh explosion complete) return exploded_scene # -------------------- 主程序入口 -------------------- def cut_glb(input_path, output_path, enginemanifold, top_k6, peg_radiusNone, peg_lengthNone, add_visual_connectorsFalse): 加载 GLB 场景执行切割合并生成凸起/孔洞并可选添加可视化连接件。 返回 (cut_scene, intersections) scene trimesh.load(input_path, forcescene) if not isinstance(scene, trimesh.Scene): mesh trimesh.load(input_path) if isinstance(mesh, trimesh.Trimesh): scene trimesh.Scene(mesh) else: raise ValueError(无法加载为场景或网格) # 过滤有效几何体 valid_geoms [] invalid_geoms [] empty_geoms [] for name, geom in scene.geometry.items(): if not isinstance(geom, trimesh.Trimesh): invalid_geoms.append((name, f类型错误: {type(geom)})) elif geom.vertices.shape[0] 0 or geom.faces.shape[0] 0: empty_geoms.append((name, f顶点:{geom.vertices.shape[0]}, 面:{geom.faces.shape[0]})) elif not is_valid_mesh(geom, check_volumeFalse): invalid_geoms.append((name, 几何结构无效)) else: valid_geoms.append(name) if not valid_geoms: raise ValueError(警告场景中没有有效的几何体) print(f有效几何体: {len(valid_geoms)} 个) if invalid_geoms: print(f⚠ 无效几何体: {len(invalid_geoms)} 个) for name, reason in invalid_geoms[:5]: print(f - {name}: {reason}) if len(invalid_geoms) 5: print(f ... 还有 {len(invalid_geoms) - 5} 个无效几何体) if empty_geoms: print(f⚠ 空几何体: {len(empty_geoms)} 个) print(f加载场景包含 {len(scene.geometry)} 个子部件) cut_scene, intersections cut_scene_geometries( scene, engineengine, top_ktop_k, peg_radiuspeg_radius, peg_lengthpeg_length, add_visual_connectorsadd_visual_connectors ) cut_scene.export(output_path) print(f切割后的场景含凸起/孔洞已保存至: {output_path}) return cut_scene, intersections if __name__ __main__: input_file rbaozha.glb # 输入文件 top_k 4 # 保留的核心部件数 output_cut fka2_{top_k}.glb output_explode fka2_explode_{top_k}.glb starttime.time() # 执行切割并生成凸起/孔洞不添加可视化连接件 cut_scene, intersections cut_glb( input_file, output_cut, enginemanifold, top_ktop_k, peg_radiusNone, # 自动计算 peg_lengthNone, # 自动计算 add_visual_connectorsFalse # 设为 True 可额外添加独立指示圆柱 ) # 打印交面信息 if intersections: total_area 0.0 print(\n 切割面面积统计全部 ) for item in intersections: if len(item) 4: name_i, name_j, center, area, normal item[:5] print(f {name_i} ∩ {name_j}: 面积 {area:.6f}, 法线 {normal}) total_area area else: print(f {item[0]} ∩ {item[1]}: 面积 (未记录)) print(f总切割面积: {total_area:.6f}) print(\n) else: print(没有检测到切割面。) # 生成爆炸视图基于切割后的场景 explode_scene explode_mesh( cut_scene, intersectionsintersections, explosion_scale0.1, area_threshold_ratio0.0 ) explode_scene.export(output_explode) print(f爆炸图已保存至: {output_explode} time: {time.time() - start})
返回列表