优化gcodeviewer.py,现在渲染非常流畅
This commit is contained in:
@@ -29,6 +29,7 @@ class AIOPrrintSystemAPI:
|
||||
# },
|
||||
# 'progress': {
|
||||
# 'completion': random.uniform(0, 100.0),
|
||||
# # 'filepos': random.randint(1234,20934490),
|
||||
# 'filepos': 20934490,
|
||||
# 'printTime': random.randint(1234,54321),
|
||||
# 'printTimeLeft': random.randint(1234,54321),
|
||||
|
||||
@@ -7,15 +7,17 @@ from PyQt6.QtOpenGL import QOpenGLShaderProgram, QOpenGLShader, QOpenGLBuffer
|
||||
class GCodeParseWorker(QThread):
|
||||
finished = pyqtSignal(dict)
|
||||
|
||||
def __init__(self, filepath, type_map, default_colors, parent=None):
|
||||
def __init__(self, filepath, type_map, default_colors, type_id_map, parent=None):
|
||||
super().__init__(parent)
|
||||
self.filepath = filepath
|
||||
self.TYPE_MAP = type_map
|
||||
self.DEFAULT_COLORS = default_colors
|
||||
self.TYPE_ID_MAP = type_id_map
|
||||
|
||||
def run(self):
|
||||
points = []
|
||||
colors = []
|
||||
filters = []
|
||||
type_segments = {}
|
||||
segment_zs = {}
|
||||
type_visibility = {}
|
||||
@@ -25,6 +27,7 @@ class GCodeParseWorker(QThread):
|
||||
feature_type = 'OTHER'
|
||||
current_segment_type = 'OTHER'
|
||||
segment_start = 0
|
||||
segment_count = {}
|
||||
type_visibility['TRAVEL'] = False
|
||||
|
||||
relative_e = False
|
||||
@@ -126,6 +129,7 @@ class GCodeParseWorker(QThread):
|
||||
# add_segment(current_segment_type, segment_start, vertex_idx - segment_start, z)
|
||||
current_segment_type = seg_type
|
||||
segment_start = vertex_idx
|
||||
segment_count[seg_type] = segment_count.get(seg_type, 0) + 1
|
||||
|
||||
if new_x < min_x: min_x = new_x
|
||||
if new_x > max_x: max_x = new_x
|
||||
@@ -150,6 +154,13 @@ class GCodeParseWorker(QThread):
|
||||
gy = int(new_y / 2.0)
|
||||
layer_grids.setdefault(grid_z,set()).add((gx, gy))
|
||||
|
||||
filt_z = round(new_z, 2) if is_extrusion else round(z, 2)
|
||||
type_id = self.TYPE_ID_MAP.get(seg_type, 8)
|
||||
curr_seg_idx = segment_count.get(seg_type, 0)
|
||||
|
||||
filters.extend([type_id, 1.0, curr_seg_idx, filt_z,
|
||||
type_id, 1.0, curr_seg_idx, filt_z])
|
||||
|
||||
points.extend([x, y, z, new_x, new_y, new_z])
|
||||
colors.extend([*c, *c])
|
||||
vertex_idx += 2
|
||||
@@ -171,6 +182,8 @@ class GCodeParseWorker(QThread):
|
||||
add_segment(current_segment_type, segment_start, seg_len, z)
|
||||
segment_visibility[(segment_start, seg_len)] = True
|
||||
|
||||
filters_np = np.array(filters, dtype=np.float32).reshape(-1, 4) if vertex_idx > 0 else np.zeros((0, 4), dtype=np.float32)
|
||||
|
||||
# 判断哪些segment在内部
|
||||
for type_name, segments in type_segments.items():
|
||||
if type_name in ('WALL-INNER', 'SUPPORT', 'FILL'):
|
||||
@@ -193,6 +206,7 @@ class GCodeParseWorker(QThread):
|
||||
if (gx+ox, gy+oy) in gird:
|
||||
neighbors += 1
|
||||
if neighbors >= 4:
|
||||
filters_np[start:start+length, 1] = 0.0
|
||||
segment_visibility[(start, length)] = False
|
||||
except Exception as e:
|
||||
pass
|
||||
@@ -210,6 +224,7 @@ class GCodeParseWorker(QThread):
|
||||
result = {
|
||||
'vertices': np.array(points, dtype=np.float32) if vertex_idx > 0 else np.zeros((0,), dtype=np.float32),
|
||||
'colors': np.array(colors, dtype=np.float32) if vertex_idx > 0 else np.zeros((0,), dtype=np.float32),
|
||||
'filters': filters_np,
|
||||
'vertex_count': vertex_idx,
|
||||
'center_x': cx,
|
||||
'center_y': cy,
|
||||
@@ -260,14 +275,91 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
'TRAVEL': (0.25, 0.31, 0.38), # 0x405060
|
||||
}
|
||||
|
||||
TYPE_ID_MAP = {
|
||||
'WALL-OUTER': 1,
|
||||
'WALL-INNER': 2,
|
||||
'SKIN': 3,
|
||||
'FILL': 4,
|
||||
'SUPPORT': 5,
|
||||
'SUPPORT-INTERFACE': 6,
|
||||
'SKIRT': 7,
|
||||
'OTHER': 8,
|
||||
'TRAVEL': 9,
|
||||
}
|
||||
|
||||
# 顶点着色器(GLSL ES 1.00)
|
||||
VERTEX_SHADER = """
|
||||
attribute vec3 aPos;
|
||||
attribute vec3 aColor;
|
||||
attribute vec4 aFilter;
|
||||
varying vec3 vColor;
|
||||
uniform mat4 uMVP;
|
||||
|
||||
uniform float uZMin;
|
||||
uniform float uZMax;
|
||||
|
||||
uniform float uVis1; // WALL-OUTER
|
||||
uniform float uVis2; // WALL-INNER
|
||||
uniform float uVis3; // SKIN
|
||||
uniform float uVis4; // FILL
|
||||
uniform float uVis5; // SUPPORT
|
||||
uniform float uVis6; // SUPPORT-INTERFACE
|
||||
uniform float uVis7; // SKIRT
|
||||
uniform float uVis8; // OTHER
|
||||
uniform float uVis9; // TRAVEL
|
||||
|
||||
uniform float uLodFactor;
|
||||
uniform float uZoomMul;
|
||||
|
||||
void main() {
|
||||
gl_Position = uMVP * vec4(aPos, 1.0);
|
||||
float doDraw = 1.0;
|
||||
float type_id = aFilter.x;
|
||||
float is_vis = aFilter.y;
|
||||
float seg_idx = aFilter.z;
|
||||
float seg_z = aFilter.w;
|
||||
|
||||
if (is_vis < 0.5) doDraw = 0.0;
|
||||
|
||||
int t = int(type_id + 0.1);
|
||||
|
||||
if (t == 2 || t == 4 || t == 5) {
|
||||
if (seg_z < uZMin || seg_z > uZMax) doDraw = 0.0;
|
||||
}
|
||||
|
||||
if (t == 1 && uVis1 < 0.5) doDraw = 0.0;
|
||||
else if (t == 2 && uVis2 < 0.5) doDraw = 0.0;
|
||||
else if (t == 3 && uVis3 < 0.5) doDraw = 0.0;
|
||||
else if (t == 4 && uVis4 < 0.5) doDraw = 0.0;
|
||||
else if (t == 5 && uVis5 < 0.5) doDraw = 0.0;
|
||||
else if (t == 6 && uVis6 < 0.5) doDraw = 0.0;
|
||||
else if (t == 7 && uVis7 < 0.5) doDraw = 0.0;
|
||||
else if (t == 8 && uVis8 < 0.5) doDraw = 0.0;
|
||||
else if (t == 9 && uVis9 < 0.5) doDraw = 0.0;
|
||||
|
||||
if (uLodFactor > 0.0) {
|
||||
float simplify_mul = 1.0;
|
||||
if (t == 4) simplify_mul = 3.0;
|
||||
else if (t == 5) simplify_mul = 4.0;
|
||||
else if (t == 9) simplify_mul = 8.0;
|
||||
|
||||
float lod_step_f = max(1.0, floor(uLodFactor * simplify_mul * uZoomMul));
|
||||
if (lod_step_f > 1.0) {
|
||||
if (mod(seg_idx, lod_step_f) != 0.0) doDraw = 0.0;
|
||||
}
|
||||
|
||||
if (uZoomMul > 1.6 && (t == 4 || t == 5 || t == 2)) {
|
||||
if (mod(floor(seg_idx / 2.0), 3.0) != 0.0) doDraw = 0.0;
|
||||
}
|
||||
if (uZoomMul > 2.8 && (t == 4 || t == 5 || t == 2)) {
|
||||
if (mod(floor(seg_idx / 2.0), 6.0) != 0.0) doDraw = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if (doDraw < 0.5) {
|
||||
gl_Position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
} else {
|
||||
gl_Position = uMVP * vec4(aPos, 1.0);
|
||||
}
|
||||
vColor = aColor;
|
||||
}
|
||||
"""
|
||||
@@ -295,9 +387,11 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
# 数据缓冲区
|
||||
self.vertices = None
|
||||
self.colors = None
|
||||
self.filters = None
|
||||
self.vertex_count = 0
|
||||
self.vbo_vertices = None
|
||||
self.vbo_colors = None
|
||||
self.vbo_filters = None
|
||||
self.vbo_ready = False
|
||||
|
||||
# 类型分段
|
||||
@@ -342,7 +436,7 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
self.uMVP_location = None
|
||||
|
||||
# 简化参数
|
||||
self.enable_lod = True
|
||||
self.enable_lod = False
|
||||
self.lod_factor = 0.0
|
||||
self.visible_top_layers = 0
|
||||
self.visible_bottom_layers = 0
|
||||
@@ -353,7 +447,7 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
self._worker.terminate()
|
||||
self._worker.wait()
|
||||
|
||||
self._worker = GCodeParseWorker(filepath, self.TYPE_MAP, self.DEFAULT_COLORS)
|
||||
self._worker = GCodeParseWorker(filepath, self.TYPE_MAP, self.DEFAULT_COLORS, self.TYPE_ID_MAP)
|
||||
self._worker.finished.connect(self._on_parse_finished)
|
||||
self._worker.start()
|
||||
|
||||
@@ -363,6 +457,7 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
|
||||
self.vertices = result['vertices']
|
||||
self.colors = result['colors']
|
||||
self.filters = result['filters']
|
||||
self.vertex_count = result['vertex_count']
|
||||
self.center_x = result['center_x']
|
||||
self.center_y = result['center_y']
|
||||
@@ -419,7 +514,7 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
self.view_rot_x = max(-90.0, min(0.0, rot_x))
|
||||
self.view_rot_z = rot_z
|
||||
if zoom is not None:
|
||||
self.view_zoom = zoom
|
||||
self.view_zoom = max(-500.0, min(-10.0, zoom))
|
||||
self.update()
|
||||
|
||||
if length == 0:
|
||||
@@ -442,12 +537,14 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
# 获取属性和 uniform 位置
|
||||
self.aPos_location = self.shader_program.attributeLocation("aPos")
|
||||
self.aColor_location = self.shader_program.attributeLocation("aColor")
|
||||
self.aFilter_location = self.shader_program.attributeLocation("aFilter")
|
||||
self.uMVP_location = self.shader_program.uniformLocation("uMVP")
|
||||
self.uDarken_location = self.shader_program.uniformLocation("uDarken")
|
||||
|
||||
# 创建缓冲对象
|
||||
self.vbo_vertices = QOpenGLBuffer(QOpenGLBuffer.Type.VertexBuffer)
|
||||
self.vbo_colors = QOpenGLBuffer(QOpenGLBuffer.Type.VertexBuffer)
|
||||
self.vbo_filters = QOpenGLBuffer(QOpenGLBuffer.Type.VertexBuffer)
|
||||
|
||||
def resizeGL(self, w, h):
|
||||
import OpenGL.GL as gl
|
||||
@@ -500,103 +597,65 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
# 允许 z-fighting 覆盖,用于同一位置多次渲染线条
|
||||
gl.glDepthFunc(gl.GL_LEQUAL)
|
||||
|
||||
count = self.progress_vertices
|
||||
|
||||
# Set uniforms for shader logic
|
||||
visible_toggles = {i: 1.0 for i in range(1, 10)}
|
||||
for name, viz in self.type_visibility.items():
|
||||
type_id = self.TYPE_ID_MAP.get(name, 8)
|
||||
visible_toggles[type_id] = 1.0 if viz else 0.0
|
||||
|
||||
for i in range(1, 10):
|
||||
loc = self.shader_program.uniformLocation(f"uVis{i}")
|
||||
if loc >= 0:
|
||||
self.shader_program.setUniformValue(loc, visible_toggles[i])
|
||||
|
||||
zoom_mul = max(1.0, abs(self.view_zoom) / 250.0)
|
||||
lod_loc = self.shader_program.uniformLocation("uLodFactor")
|
||||
self.shader_program.setUniformValue(lod_loc, float(self.lod_factor) if getattr(self, 'enable_lod', True) else 0.0)
|
||||
zm_loc = self.shader_program.uniformLocation("uZoomMul")
|
||||
self.shader_program.setUniformValue(zm_loc, float(zoom_mul))
|
||||
|
||||
top_limit = self.max_z - self.visible_top_layers * self.layer_height if getattr(self, 'visible_top_layers', 0) > 0 else 9999.0
|
||||
bottom_limit = self.min_z + self.visible_bottom_layers * self.layer_height if getattr(self, 'visible_bottom_layers', 0) > 0 else -9999.0
|
||||
uzmin_loc = self.shader_program.uniformLocation("uZMin")
|
||||
uzmax_loc = self.shader_program.uniformLocation("uZMax")
|
||||
self.shader_program.setUniformValue(uzmin_loc, float(bottom_limit))
|
||||
self.shader_program.setUniformValue(uzmax_loc, float(top_limit))
|
||||
|
||||
self.vbo_filters.bind()
|
||||
self.shader_program.setAttributeBuffer(self.aFilter_location, gl.GL_FLOAT, 0, 4, 0)
|
||||
self.shader_program.enableAttributeArray(self.aFilter_location)
|
||||
|
||||
# 渲染两次:一次绘制加粗加深的边界底线,一次绘制正常宽度的原色骨架线
|
||||
# (在树莓派等性能有限的平台上,使用真实的3D圆柱/方块代替线条会导致顶点数暴增十几倍直接卡顿,
|
||||
# 因此通过动态加粗像素级线宽来性能无损地模拟出“体积感”)
|
||||
for pass_idx in range(2):
|
||||
if pass_idx == 0:
|
||||
if self.enable_lod:
|
||||
if getattr(self, 'enable_lod', True):
|
||||
gl.glLineWidth(3.0) # 底线宽度(加大以模拟体积轮廓)
|
||||
else:
|
||||
gl.glLineWidth(6.0) # 底线宽度(加大以模拟体积轮廓)
|
||||
self.shader_program.setUniformValue(self.uDarken_location, 0.8) # 加深颜色至 40% 亮度
|
||||
else:
|
||||
if self.enable_lod:
|
||||
if getattr(self, 'enable_lod', True):
|
||||
gl.glLineWidth(1.5) # 主体宽度(加大以模拟线条厚度)
|
||||
else:
|
||||
gl.glLineWidth(3.0) # 主体宽度(加大以模拟线条厚度)
|
||||
self.shader_program.setUniformValue(self.uDarken_location, 1.0) # 保持原色
|
||||
|
||||
# 按类型分段绘制
|
||||
# for type_name, segments in self.type_segments.items():
|
||||
# if not self.type_visibility.get(type_name, True):
|
||||
# continue
|
||||
# for start, length in segments:
|
||||
# if start >= self.progress_vertices:
|
||||
# continue
|
||||
# end = start + length
|
||||
# visible_start = start
|
||||
# visible_count = length
|
||||
# if end > self.progress_vertices:
|
||||
# visible_count = self.progress_vertices - start
|
||||
# if visible_count > 0:
|
||||
# gl.glDrawArrays(gl.GL_LINES, visible_start, visible_count)
|
||||
|
||||
# 按类型分段绘制(带LOD)
|
||||
for type_name, segments in self.type_segments.items():
|
||||
if not self.type_visibility.get(type_name, True):
|
||||
continue
|
||||
# 不同类型的简化倍率
|
||||
simplify_mul = 1
|
||||
if type_name == 'FILL':
|
||||
simplify_mul = 3
|
||||
elif type_name == 'SUPPORT':
|
||||
simplify_mul = 4
|
||||
elif type_name == 'TRAVEL':
|
||||
simplify_mul = 8
|
||||
# 根据缩放动态增强简化
|
||||
zoom_mul = max(1.0, abs(self.view_zoom) / 250.0)
|
||||
# 最终步进
|
||||
lod_step = 1
|
||||
if self.enable_lod:
|
||||
lod_step = int(max(1, self.lod_factor * simplify_mul * zoom_mul))
|
||||
seg_index = 0
|
||||
for start, length in segments:
|
||||
if not self.segment_visibility.get((start, length), True):
|
||||
continue
|
||||
if type_name in ('WALL-INNER', 'SUPPORT', 'FILL'):
|
||||
seg_z = self.segment_zs.get((start, length), self.center_z)
|
||||
top_limit = self.max_z - self.visible_top_layers * self.layer_height if self.visible_top_layers > 0 else self.min_z - 1.0
|
||||
bottom_limit = self.min_z + self.visible_bottom_layers * self.layer_height if self.visible_bottom_layers > 0 else self.max_z + 1.0
|
||||
if bottom_limit <= seg_z <= top_limit:
|
||||
continue
|
||||
|
||||
seg_index += 1
|
||||
# LOD抽样
|
||||
if lod_step > 1:
|
||||
if (seg_index % lod_step) != 0:
|
||||
continue
|
||||
if start >= self.progress_vertices:
|
||||
continue
|
||||
|
||||
end = start + length
|
||||
visible_start = start
|
||||
visible_count = length
|
||||
if end > self.progress_vertices:
|
||||
visible_count = self.progress_vertices - start
|
||||
if visible_count <= 0:
|
||||
continue
|
||||
|
||||
# 超远距离时进一步减少绘制长度
|
||||
if self.enable_lod:
|
||||
if type_name in ('FILL','SUPPORT','WALL-INNER'):
|
||||
if abs(self.view_zoom) > 400:
|
||||
if(start // 2) % 3 != 0:
|
||||
continue
|
||||
if abs(self.view_zoom) > 700:
|
||||
if(start // 2) % 6 != 0:
|
||||
continue
|
||||
# visible_count = visible_count // 2
|
||||
if visible_count > 0:
|
||||
gl.glDrawArrays(gl.GL_LINES, visible_start, visible_count)
|
||||
if count > 0:
|
||||
gl.glDrawArrays(gl.GL_LINES, 0, count)
|
||||
|
||||
# 恢复默认深度测试模式
|
||||
gl.glDepthFunc(gl.GL_LESS)
|
||||
|
||||
self.shader_program.disableAttributeArray(self.aPos_location)
|
||||
self.shader_program.disableAttributeArray(self.aColor_location)
|
||||
self.shader_program.disableAttributeArray(self.aFilter_location)
|
||||
self.vbo_vertices.release()
|
||||
self.vbo_colors.release()
|
||||
self.vbo_filters.release()
|
||||
self.shader_program.release()
|
||||
|
||||
def _create_vbos(self):
|
||||
@@ -604,6 +663,8 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
self.vbo_vertices.destroy()
|
||||
if self.vbo_colors.isCreated():
|
||||
self.vbo_colors.destroy()
|
||||
if self.vbo_filters.isCreated():
|
||||
self.vbo_filters.destroy()
|
||||
|
||||
self.vbo_vertices.create()
|
||||
self.vbo_vertices.bind()
|
||||
@@ -614,6 +675,11 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
self.vbo_colors.bind()
|
||||
self.vbo_colors.allocate(self.colors.tobytes(), self.colors.nbytes)
|
||||
self.vbo_colors.release()
|
||||
|
||||
self.vbo_filters.create()
|
||||
self.vbo_filters.bind()
|
||||
self.vbo_filters.allocate(self.filters.tobytes(), self.filters.nbytes)
|
||||
self.vbo_filters.release()
|
||||
|
||||
# ── 触摸/鼠标交互(完全不变) ──
|
||||
def mousePressEvent(self, event):
|
||||
@@ -634,6 +700,7 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
def wheelEvent(self, event):
|
||||
delta = event.angleDelta().y() / 120
|
||||
self.view_zoom += delta * 10
|
||||
self.view_zoom = max(-500.0, min(-10.0, self.view_zoom))
|
||||
self.update()
|
||||
|
||||
def event(self, e):
|
||||
@@ -696,6 +763,7 @@ class GCodeViewerWidget(QOpenGLWidget):
|
||||
if self._pinch_start_dist > 0:
|
||||
scale = dist / self._pinch_start_dist
|
||||
self.view_zoom = self._pinch_start_zoom * (1 / scale)
|
||||
self.view_zoom = max(-500.0, min(-10.0, self.view_zoom))
|
||||
|
||||
# 平移 (双指并行移动)
|
||||
dcx = center_x - self._pinch_start_center[0]
|
||||
|
||||
Reference in New Issue
Block a user