diff --git a/plugins/SimulationView/SimulationPass.py b/plugins/SimulationView/SimulationPass.py index 506bc5a01da..2754fb5d949 100644 --- a/plugins/SimulationView/SimulationPass.py +++ b/plugins/SimulationView/SimulationPass.py @@ -65,7 +65,7 @@ def render(self): self._layer_shader.setUniformValue("u_active_extruder", float(max(0, self._extruder_manager.activeExtruderIndex))) if not self._compatibility_mode: self._layer_shader.setUniformValue("u_starts_color", Color(*Application.getInstance().getTheme().getColor("layerview_starts").getRgb())) - + if self._layer_view: self._layer_shader.setUniformValue("u_max_feedrate", self._layer_view.getMaxFeedrate()) self._layer_shader.setUniformValue("u_min_feedrate", self._layer_view.getMinFeedrate()) @@ -73,6 +73,8 @@ def render(self): self._layer_shader.setUniformValue("u_min_thickness", self._layer_view.getMinThickness()) self._layer_shader.setUniformValue("u_max_line_width", self._layer_view.getMaxLineWidth()) self._layer_shader.setUniformValue("u_min_line_width", self._layer_view.getMinLineWidth()) + self._layer_shader.setUniformValue("u_max_flow_rate", self._layer_view.getMaxFlowRate()) + self._layer_shader.setUniformValue("u_min_flow_rate", self._layer_view.getMinFlowRate()) self._layer_shader.setUniformValue("u_layer_view_type", self._layer_view.getSimulationViewType()) self._layer_shader.setUniformValue("u_extruder_opacity", self._layer_view.getExtruderOpacities()) self._layer_shader.setUniformValue("u_show_travel_moves", self._layer_view.getShowTravelMoves()) @@ -86,6 +88,8 @@ def render(self): self._layer_shader.setUniformValue("u_min_feedrate", 0) self._layer_shader.setUniformValue("u_max_thickness", 1) self._layer_shader.setUniformValue("u_min_thickness", 0) + self._layer_shader.setUniformValue("u_max_flow_rate", 1) + self._layer_shader.setUniformValue("u_min_flow_rate", 0) self._layer_shader.setUniformValue("u_max_line_width", 1) self._layer_shader.setUniformValue("u_min_line_width", 0) self._layer_shader.setUniformValue("u_layer_view_type", 1) @@ -174,9 +178,9 @@ def render(self): self._switching_layers = True # The first line does not have a previous line: add a MoveCombingType in front for start detection - # this way the first start of the layer can also be drawn + # this way the first start of the layer can also be drawn prev_line_types = numpy.concatenate([numpy.asarray([LayerPolygon.MoveCombingType], dtype = numpy.float32), layer_data._attributes["line_types"]["value"]]) - # Remove the last element + # Remove the last element prev_line_types = prev_line_types[0:layer_data._attributes["line_types"]["value"].size] layer_data._attributes["prev_line_types"] = {'opengl_type': 'float', 'value': prev_line_types, 'opengl_name': 'a_prev_line_type'} diff --git a/plugins/SimulationView/SimulationView.py b/plugins/SimulationView/SimulationView.py index 415e49f71ce..dc3d0ef0d09 100644 --- a/plugins/SimulationView/SimulationView.py +++ b/plugins/SimulationView/SimulationView.py @@ -411,6 +411,15 @@ def getMinLineWidth(self) -> float: return 0.0 # If it's still max-float, there are no measurements. Use 0 then. return self._min_line_width + def getMaxFlowRate(self) -> float: + return self._max_line_width * self._max_thickness * self._max_feedrate + + def getMinFlowRate(self) -> float: + min_flow_rate = self._min_line_width * self._min_thickness * self._min_feedrate + if abs(min_flow_rate - sys.float_info.max) < 10: # Some lenience due to floating point rounding. + return 0.0 # If it's still max-float, there are no measurements. Use 0 then. + return min_flow_rate + def calculateMaxLayers(self) -> None: """ Calculates number of layers, triggers signals if the number of layers changed and makes sure the top layers are diff --git a/plugins/SimulationView/SimulationViewMenuComponent.qml b/plugins/SimulationView/SimulationViewMenuComponent.qml index d7552a4c305..e52463be934 100644 --- a/plugins/SimulationView/SimulationViewMenuComponent.qml +++ b/plugins/SimulationView/SimulationViewMenuComponent.qml @@ -90,6 +90,7 @@ Cura.ExpandableComponent property bool show_feedrate_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 2 property bool show_thickness_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 3 property bool show_line_width_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 4 + property bool show_flow_rate_gradient: show_gradient && UM.Preferences.getValue("layerview/layer_view_type") == 5 property bool only_show_top_layers: UM.Preferences.getValue("view/only_show_top_layers") property int top_layer_count: UM.Preferences.getValue("view/top_layer_count") @@ -125,6 +126,10 @@ Cura.ExpandableComponent text: catalog.i18nc("@label:listbox", "Line Width"), type_id: 4 }) + layerViewTypes.append({ + text: catalog.i18nc("@label:listbox", "Flow"), + type_id: 5 + }) } ComboBox @@ -150,10 +155,13 @@ Cura.ExpandableComponent { // Update the visibility of the legends. viewSettings.show_legend = UM.SimulationView.compatibilityMode || (type_id == 1); - viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && (type_id == 2 || type_id == 3 || type_id == 4); + viewSettings.show_gradient = !UM.SimulationView.compatibilityMode && + (type_id == 2 || type_id == 3 || type_id == 4 || type_id == 5) ; + viewSettings.show_feedrate_gradient = viewSettings.show_gradient && (type_id == 2); viewSettings.show_thickness_gradient = viewSettings.show_gradient && (type_id == 3); viewSettings.show_line_width_gradient = viewSettings.show_gradient && (type_id == 4); + viewSettings.show_flow_rate_gradient = viewSettings.show_gradient && (type_id == 5); } } @@ -396,11 +404,17 @@ Cura.ExpandableComponent { return parseFloat(UM.SimulationView.minThickness).toFixed(2) } - //Line width selected + // Line width selected if(UM.Preferences.getValue("layerview/layer_view_type") == 4) { return parseFloat(UM.SimulationView.minLineWidth).toFixed(2); } + // Flow Rate selected + if(UM.Preferences.getValue("layerview/layer_view_type") == 5) + { + return parseFloat(UM.SimulationView.getMinFlowRate()).toFixed(2); + } + } return catalog.i18nc("@label","min") } @@ -431,6 +445,11 @@ Cura.ExpandableComponent { return "mm" } + // Flow Rate selected + if (UM.Preferences.getValue("layerview/layer_view_type") == 5) + { + return "mm³/s" + } } return "" } @@ -460,6 +479,11 @@ Cura.ExpandableComponent { return parseFloat(UM.SimulationView.maxLineWidth).toFixed(2); } + // Flow rate selected + if(UM.Preferences.getValue("layerview/layer_view_type") == 5) + { + return parseFloat(UM.SimulationView.getMaxFlowRate()).toFixed(2); + } } return catalog.i18nc("@label","max") } @@ -474,7 +498,10 @@ Cura.ExpandableComponent Rectangle { id: feedrateGradient - visible: viewSettings.show_feedrate_gradient || viewSettings.show_line_width_gradient + visible: ( + viewSettings.show_feedrate_gradient || + viewSettings.show_line_width_gradient + ) anchors.left: parent.left anchors.right: parent.right height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) @@ -526,7 +553,9 @@ Cura.ExpandableComponent Rectangle { id: thicknessGradient - visible: viewSettings.show_thickness_gradient + visible: ( + viewSettings.show_thickness_gradient + ) anchors.left: parent.left anchors.right: parent.right height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) @@ -578,6 +607,85 @@ Cura.ExpandableComponent } } } + + // Gradient colors for flow (similar to jet colormap) + Rectangle + { + id: jetGradient + visible: ( + viewSettings.show_flow_rate_gradient + ) + anchors.left: parent.left + anchors.right: parent.right + height: Math.round(UM.Theme.getSize("layerview_row").height * 1.5) + border.width: UM.Theme.getSize("default_lining").width + border.color: UM.Theme.getColor("lining") + + LinearGradient + { + anchors + { + left: parent.left + leftMargin: UM.Theme.getSize("default_lining").width + right: parent.right + rightMargin: UM.Theme.getSize("default_lining").width + top: parent.top + topMargin: UM.Theme.getSize("default_lining").width + bottom: parent.bottom + bottomMargin: UM.Theme.getSize("default_lining").width + } + start: Qt.point(0, 0) + end: Qt.point(parent.width, 0) + gradient: Gradient + { + GradientStop + { + position: 0.0 + color: Qt.rgba(0, 0, 0.5, 1) + } + GradientStop + { + position: 0.125 + color: Qt.rgba(0, 0.0, 1.0, 1) + } + GradientStop + { + position: 0.25 + color: Qt.rgba(0, 0.5, 1.0, 1) + } + GradientStop + { + position: 0.375 + color: Qt.rgba(0.0, 1.0, 1.0, 1) + } + GradientStop + { + position: 0.5 + color: Qt.rgba(0.5, 1.0, 0.5, 1) + } + GradientStop + { + position: 0.625 + color: Qt.rgba(1.0, 1.0, 0.0, 1) + } + GradientStop + { + position: 0.75 + color: Qt.rgba(1.0, 0.5, 0, 1) + } + GradientStop + { + position: 0.875 + color: Qt.rgba(1.0, 0.0, 0, 1) + } + GradientStop + { + position: 1.0 + color: Qt.rgba(0.5, 0, 0, 1) + } + } + } + } } FontMetrics diff --git a/plugins/SimulationView/SimulationViewProxy.py b/plugins/SimulationView/SimulationViewProxy.py index bdf787ab3ab..e07d63e713a 100644 --- a/plugins/SimulationView/SimulationViewProxy.py +++ b/plugins/SimulationView/SimulationViewProxy.py @@ -126,6 +126,15 @@ def maxLineWidth(self): def minLineWidth(self): return self._simulation_view.getMinLineWidth() + @pyqtSlot(result=float) + def getMaxFlowRate(self): + return self._simulation_view.getMaxFlowRate() + + @pyqtSlot(result=float) + def getMinFlowRate(self): + return self._simulation_view.getMinFlowRate() + + # Opacity 0..1 @pyqtSlot(int, float) def setExtruderOpacity(self, extruder_nr, opacity): diff --git a/plugins/SimulationView/layers3d.shader b/plugins/SimulationView/layers3d.shader index a3178997d71..794f31ea103 100644 --- a/plugins/SimulationView/layers3d.shader +++ b/plugins/SimulationView/layers3d.shader @@ -12,6 +12,8 @@ vertex41core = uniform lowp float u_min_thickness; uniform lowp float u_max_line_width; uniform lowp float u_min_line_width; + uniform lowp float u_max_flow_rate; + uniform lowp float u_min_flow_rate; uniform lowp int u_layer_view_type; uniform lowp mat4 u_extruder_opacity; // currently only for max 16 extruders, others always visible @@ -105,6 +107,30 @@ vertex41core = return vec4(red, green, blue, 1.0); } + float clamp(float v) + { + float t = v < 0 ? 0 : v; + return t > 1.0 ? 1.0 : t; + } + + // Inspired by https://stackoverflow.com/a/46628410 + vec4 flowRateGradientColor(float abs_value, float min_value, float max_value) + { + float t; + if(abs(min_value - max_value) < 0.0001) + { + t = 0; + } + else + { + t = 2.0 * ((abs_value - min_value) / (max_value - min_value)) - 1; + } + float red = clamp(1.5 - abs(2.0 * t - 1.0)); + float green = clamp(1.5 - abs(2.0 * t)); + float blue = clamp(1.5 - abs(2.0 * t + 1.0)); + return vec4(red, green, blue, 1.0); + } + void main() { vec4 v1_vertex = a_vertex; @@ -130,6 +156,10 @@ vertex41core = case 4: // "Line width" v_color = lineWidthGradientColor(a_line_dim.x, u_min_line_width, u_max_line_width); break; + case 5: // "Flow" + float flow_rate = a_line_dim.x * a_line_dim.y * a_feedrate; + v_color = flowRateGradientColor(flow_rate, u_min_flow_rate, u_max_flow_rate); + break; } v_vertex = world_space_vert.xyz; @@ -318,7 +348,6 @@ geometry41core = EndPrimitive(); } - if ((u_show_starts == 1) && (v_prev_line_type[0] != 1) && (v_line_type[0] == 1)) { float w = size_x; float h = size_y; @@ -337,7 +366,7 @@ geometry41core = myEmitVertex(v_vertex[0] + vec3(-w, -h, -w), u_starts_color, normalize(vec3(-1.0, -1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, -h, -w, 0.0))); // Back-bottom-right myEmitVertex(v_vertex[0] + vec3( w, h, -w), u_starts_color, normalize(vec3( 1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4( w, h, -w, 0.0))); // Back-top-left myEmitVertex(v_vertex[0] + vec3(-w, h, -w), u_starts_color, normalize(vec3(-1.0, 1.0, -1.0)), viewProjectionMatrix * (gl_in[0].gl_Position + vec4(-w, h, -w, 0.0))); // Back-top-right - + EndPrimitive(); } }