handle_simple.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. from PyQt5.QtCore import QObject, pyqtSlot
  2. import open3d as o3d
  3. import numpy as np
  4. import os
  5. from OCC.Core.TopoDS import TopoDS_Face
  6. class Datahandle(QObject):
  7. def __init__(self):
  8. super().__init__()
  9. self.qml_item = None
  10. self.vertices, self.colors = self.load_data() # 预加载数据
  11. self.colors = np.array([])
  12. self.triangles = np.array([]) # 新增:三角面索引
  13. self.normals = np.array([]) # 新增:法线
  14. def load_data(self):
  15. """
  16. 加载 3D 数据(这里用随机点云代替实际文件)
  17. 返回: vertices, colors (numpy arrays)
  18. """
  19. pcd = o3d.geometry.PointCloud()
  20. pcd.points = o3d.utility.Vector3dVector(np.random.rand(5000, 3))
  21. pcd.colors = o3d.utility.Vector3dVector(np.random.rand(5000, 3))
  22. vertices = np.asarray(pcd.points)
  23. colors = np.asarray(pcd.colors)
  24. return vertices, colors
  25. @pyqtSlot('QVariant')
  26. def set3DItem(self, item):
  27. """接收 QML 中的 QML3DItem 实例"""
  28. self.qml_item = item
  29. if self.qml_item:
  30. self.qml_item.set_data(self.vertices, self.colors)
  31. print("✅ 数据已传递给 QML3DItem")
  32. @pyqtSlot(float, float, float)
  33. def onPointPicked(self, x, y, z):
  34. """接收从 QML3DItem 发出的拾取信号"""
  35. print(f"✅ 拾取到点: ({x:.3f}, {y:.3f}, {z:.3f})")
  36. # 这里可以扩展:保存坐标、发送到其他模块等
  37. class Dataload(QObject):
  38. def __init__(self):
  39. super().__init__()
  40. self.qml_item = None
  41. self.vertices = np.array([])
  42. self.colors = np.array([])
  43. self.triangles = np.array([]) # 新增:三角面索引
  44. self.normals = np.array([]) # 新增:法线
  45. def load_data_from_file(self, file_path: str):
  46. """
  47. 使用 numpy-stl 自动识别 STL 格式(兼容旧版本)
  48. """
  49. print(f"📁 正在加载: {file_path}")
  50. if not os.path.exists(file_path):
  51. print(f"❌ 文件不存在: {file_path}")
  52. return np.array([]), np.array([]), np.array([]), np.array([])
  53. ext = os.path.splitext(file_path)[1].lower()
  54. if ext != '.stl':
  55. print(f"❌ 不支持的格式: {ext}")
  56. return np.array([]), np.array([]), np.array([]), np.array([])
  57. try:
  58. from stl import mesh as stl_mesh
  59. # ✅ 自动识别格式(旧版本也支持)
  60. stl_data = stl_mesh.Mesh.from_file(file_path)
  61. # 获取三角面 (N, 3, 3)
  62. vectors = stl_data.vectors.astype(np.float32)
  63. print(f"✅ 加载 {len(vectors)} 个三角面")
  64. # 展平顶点
  65. vertices = vectors.reshape(-1, 3)
  66. print(f"原始顶点数: {len(vertices)}")
  67. # 三角面索引
  68. num_triangles = len(vectors)
  69. triangles = np.arange(num_triangles * 3, dtype=np.int32).reshape(-1, 3)
  70. print(f"三角面数量: {num_triangles}")
  71. # 法线(每个三角面对应一个法线)
  72. normals = stl_data.normals.astype(np.float32)
  73. vertex_normals = np.repeat(normals, 3, axis=0) # 每个顶点复制一次
  74. print(f"法线数量: {len(vertex_normals)}")
  75. # 颜色:使用法线生成
  76. colors = (vertex_normals + 1.0) / 2.0
  77. if np.any(np.isnan(colors)) or np.any(np.isinf(colors)):
  78. colors = np.ones_like(vertex_normals) * 0.8
  79. # --- 归一化 ---
  80. if len(vertices) > 0:
  81. min_coords = np.min(vertices, axis=0)
  82. max_coords = np.max(vertices, axis=0)
  83. center = (min_coords + max_coords) / 2.0
  84. scale = np.max(max_coords - min_coords)
  85. if scale > 0:
  86. vertices = (vertices - center) / scale * 2.0
  87. z_range = np.max(vertices[:, 2]) - np.min(vertices[:, 2])
  88. if z_range < 0.1:
  89. z_center = (np.min(vertices[:, 2]) + np.max(vertices[:, 2])) / 2.0
  90. vertices[:, 2] = (vertices[:, 2] - z_center) * (0.2 / z_range) + z_center
  91. print(f"调整Z轴范围: {z_range:.6f} -> 0.2")
  92. print(f"✅ 加载成功: {file_path}")
  93. return vertices, colors, triangles, vertex_normals
  94. except Exception as e:
  95. print(f"❌ 加载失败: {e}")
  96. return np.array([]), np.array([]), np.array([]), np.array([])