Browse Source

Merge branch 'layer_view_statistic_limits_only_visible' of github.com:Ultimaker/Cura

Jaime van Kessel 3 years ago
parent
commit
eb3bdfc18a

+ 94 - 22
plugins/SimulationView/SimulationView.py

@@ -30,6 +30,7 @@ from UM.View.GL.ShaderProgram import ShaderProgram
 
 from UM.i18n import i18nCatalog
 from cura.CuraView import CuraView
+from cura.LayerPolygon import LayerPolygon  # To distinguish line types.
 from cura.Scene.ConvexHullNode import ConvexHullNode
 from cura.CuraApplication import CuraApplication
 
@@ -115,6 +116,7 @@ class SimulationView(CuraView):
         Application.getInstance().getPreferences().addPreference("layerview/show_infill", True)
         Application.getInstance().getPreferences().addPreference("layerview/show_starts", True)
 
+        self.visibleStructuresChanged.connect(self.calculateColorSchemeLimits)
         self._updateWithPreferences()
 
         self._solid_layers = int(Application.getInstance().getPreferences().getValue("view/top_layer_count"))
@@ -198,6 +200,7 @@ class SimulationView(CuraView):
         if node.getMeshData() is None:
             return
         self.setActivity(False)
+        self.calculateColorSchemeLimits()
         self.calculateMaxLayers()
         self.calculateMaxPathsOnLayer(self._current_layer_num)
 
@@ -218,12 +221,6 @@ class SimulationView(CuraView):
     def resetLayerData(self) -> None:
         self._current_layer_mesh = None
         self._current_layer_jumps = None
-        self._max_feedrate = sys.float_info.min
-        self._min_feedrate = sys.float_info.max
-        self._max_thickness = sys.float_info.min
-        self._min_thickness = sys.float_info.max
-        self._max_line_width = sys.float_info.min
-        self._min_line_width = sys.float_info.max
 
     def beginRendering(self) -> None:
         scene = self.getController().getScene()
@@ -334,37 +331,52 @@ class SimulationView(CuraView):
         # If more than 16 extruders are called for, this should be converted to a sampler1d.
         return Matrix(self._extruder_opacity)
 
-    def setShowTravelMoves(self, show):
+    def setShowTravelMoves(self, show: bool) -> None:
+        if show == self._show_travel_moves:
+            return
         self._show_travel_moves = show
         self.currentLayerNumChanged.emit()
+        self.visibleStructuresChanged.emit()
 
-    def getShowTravelMoves(self):
+    def getShowTravelMoves(self) -> bool:
         return self._show_travel_moves
 
     def setShowHelpers(self, show: bool) -> None:
+        if show == self._show_helpers:
+            return
         self._show_helpers = show
         self.currentLayerNumChanged.emit()
+        self.visibleStructuresChanged.emit()
 
     def getShowHelpers(self) -> bool:
         return self._show_helpers
 
     def setShowSkin(self, show: bool) -> None:
+        if show == self._show_skin:
+            return
         self._show_skin = show
         self.currentLayerNumChanged.emit()
+        self.visibleStructuresChanged.emit()
 
     def getShowSkin(self) -> bool:
         return self._show_skin
 
     def setShowInfill(self, show: bool) -> None:
+        if show == self._show_infill:
+            return
         self._show_infill = show
         self.currentLayerNumChanged.emit()
+        self.visibleStructuresChanged.emit()
 
     def getShowInfill(self) -> bool:
         return self._show_infill
 
     def setShowStarts(self, show: bool) -> None:
+        if show == self._show_starts:
+            return
         self._show_starts = show
         self.currentLayerNumChanged.emit()
+        self.visibleStructuresChanged.emit()
 
     def getShowStarts(self) -> bool:
         return self._show_starts
@@ -400,11 +412,14 @@ class SimulationView(CuraView):
         return self._min_line_width
 
     def calculateMaxLayers(self) -> None:
+        """
+        Calculates number of layers, triggers signals if the number of layers changed and makes sure the top layers are
+        recalculated for legacy layer view.
+        """
         scene = self.getController().getScene()
 
         self._old_max_layers = self._max_layers
         new_max_layers = -1
-        """Recalculate num max layers"""
         for node in DepthFirstIterator(scene.getRoot()):  # type: ignore
             layer_data = node.callDecoration("getLayerData")
             if not layer_data:
@@ -419,19 +434,6 @@ class SimulationView(CuraView):
                 if len(layer_data.getLayer(layer_id).polygons) < 1:
                     continue
 
-                # Store the max and min feedrates and thicknesses for display purposes
-                for p in layer_data.getLayer(layer_id).polygons:
-                    self._max_feedrate = max(float(p.lineFeedrates.max()), self._max_feedrate)
-                    self._min_feedrate = min(float(p.lineFeedrates.min()), self._min_feedrate)
-                    self._max_line_width = max(float(p.lineWidths.max()), self._max_line_width)
-                    self._min_line_width = min(float(p.lineWidths.min()), self._min_line_width)
-                    self._max_thickness = max(float(p.lineThicknesses.max()), self._max_thickness)
-                    try:
-                        self._min_thickness = min(float(p.lineThicknesses[numpy.nonzero(p.lineThicknesses)].min()), self._min_thickness)
-                    except ValueError:
-                        # Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding
-                        # the zero) can't be calculated
-                        Logger.log("i", "Min thickness can't be calculated because all the values are zero")
                 if max_layer_number < layer_id:
                     max_layer_number = layer_id
                 if min_layer_number > layer_id:
@@ -455,6 +457,73 @@ class SimulationView(CuraView):
                 self.maxLayersChanged.emit()
         self._startUpdateTopLayers()
 
+    def calculateColorSchemeLimits(self) -> None:
+        """
+        Calculates the limits of the colour schemes, depending on the layer view data that is visible to the user.
+        """
+        # Before we start, save the old values so that we can tell if any of the spectrums need to change.
+        old_min_feedrate = self._min_feedrate
+        old_max_feedrate = self._max_feedrate
+        old_min_linewidth = self._min_line_width
+        old_max_linewidth = self._max_line_width
+        old_min_thickness = self._min_thickness
+        old_max_thickness = self._max_thickness
+
+        self._min_feedrate = sys.float_info.max
+        self._max_feedrate = sys.float_info.min
+        self._min_line_width = sys.float_info.max
+        self._max_line_width = sys.float_info.min
+        self._min_thickness = sys.float_info.max
+        self._max_thickness = sys.float_info.min
+
+        # The colour scheme is only influenced by the visible lines, so filter the lines by if they should be visible.
+        visible_line_types = []
+        if self.getShowSkin():  # Actually "shell".
+            visible_line_types.append(LayerPolygon.SkinType)
+            visible_line_types.append(LayerPolygon.Inset0Type)
+            visible_line_types.append(LayerPolygon.InsetXType)
+        if self.getShowInfill():
+            visible_line_types.append(LayerPolygon.InfillType)
+        if self.getShowHelpers():
+            visible_line_types.append(LayerPolygon.PrimeTowerType)
+            visible_line_types.append(LayerPolygon.SkirtType)
+            visible_line_types.append(LayerPolygon.SupportType)
+            visible_line_types.append(LayerPolygon.SupportInfillType)
+            visible_line_types.append(LayerPolygon.SupportInterfaceType)
+        if self.getShowTravelMoves():
+            visible_line_types.append(LayerPolygon.MoveCombingType)
+            visible_line_types.append(LayerPolygon.MoveRetractionType)
+
+        for node in DepthFirstIterator(self.getController().getScene().getRoot()):
+            layer_data = node.callDecoration("getLayerData")
+            if not layer_data:
+                continue
+
+            for layer_index in layer_data.getLayers():
+                for polyline in layer_data.getLayer(layer_index).polygons:
+                    is_visible = numpy.isin(polyline.types, visible_line_types)
+                    visible_indices = numpy.where(is_visible)[0]
+                    if visible_indices.size == 0:  # No items to take maximum or minimum of.
+                        continue
+                    visible_feedrates = numpy.take(polyline.lineFeedrates, visible_indices)
+                    visible_linewidths = numpy.take(polyline.lineWidths, visible_indices)
+                    visible_thicknesses = numpy.take(polyline.lineThicknesses, visible_indices)
+                    self._max_feedrate = max(float(visible_feedrates.max()), self._max_feedrate)
+                    self._min_feedrate = min(float(visible_feedrates.min()), self._min_feedrate)
+                    self._max_line_width = max(float(visible_linewidths.max()), self._max_line_width)
+                    self._min_line_width = min(float(visible_linewidths.min()), self._min_line_width)
+                    self._max_thickness = max(float(visible_thicknesses.max()), self._max_thickness)
+                    try:
+                        self._min_thickness = min(float(visible_thicknesses[numpy.nonzero(visible_thicknesses)].min()), self._min_thickness)
+                    except ValueError:
+                        # Sometimes, when importing a GCode the line thicknesses are zero and so the minimum (avoiding the zero) can't be calculated.
+                        Logger.log("i", "Min thickness can't be calculated because all the values are zero")
+
+        if old_min_feedrate != self._min_feedrate or old_max_feedrate != self._max_feedrate \
+                or old_min_linewidth != self._min_line_width or old_max_linewidth != self._max_line_width \
+                or old_min_thickness != self._min_thickness or old_max_thickness != self._max_thickness:
+            self.colorSchemeLimitsChanged.emit()
+
     def calculateMaxPathsOnLayer(self, layer_num: int) -> None:
         # Update the currentPath
         scene = self.getController().getScene()
@@ -481,6 +550,8 @@ class SimulationView(CuraView):
     preferencesChanged = Signal()
     busyChanged = Signal()
     activityChanged = Signal()
+    visibleStructuresChanged = Signal()
+    colorSchemeLimitsChanged = Signal()
 
     def getProxy(self, engine, script_engine):
         """Hackish way to ensure the proxy is already created
@@ -512,6 +583,7 @@ class SimulationView(CuraView):
             Application.getInstance().getPreferences().preferenceChanged.connect(self._onPreferencesChanged)
             self._controller.getScene().getRoot().childrenChanged.connect(self._onSceneChanged)
 
+            self.calculateColorSchemeLimits()
             self.calculateMaxLayers()
             self.calculateMaxPathsOnLayer(self._current_layer_num)
 

+ 6 - 6
plugins/SimulationView/SimulationViewMenuComponent.qml

@@ -389,17 +389,17 @@ Cura.ExpandableComponent
                         // Feedrate selected
                         if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
                         {
-                            return parseFloat(UM.SimulationView.getMinFeedrate()).toFixed(2)
+                            return parseFloat(UM.SimulationView.minFeedrate).toFixed(2)
                         }
                         // Layer thickness selected
                         if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
                         {
-                            return parseFloat(UM.SimulationView.getMinThickness()).toFixed(2)
+                            return parseFloat(UM.SimulationView.minThickness).toFixed(2)
                         }
                         //Line width selected
                         if(UM.Preferences.getValue("layerview/layer_view_type") == 4)
                         {
-                            return parseFloat(UM.SimulationView.getMinLineWidth()).toFixed(2);
+                            return parseFloat(UM.SimulationView.minLineWidth).toFixed(2);
                         }
                     }
                     return catalog.i18nc("@label","min")
@@ -448,17 +448,17 @@ Cura.ExpandableComponent
                         // Feedrate selected
                         if (UM.Preferences.getValue("layerview/layer_view_type") == 2)
                         {
-                            return parseFloat(UM.SimulationView.getMaxFeedrate()).toFixed(2)
+                            return parseFloat(UM.SimulationView.maxFeedrate).toFixed(2)
                         }
                         // Layer thickness selected
                         if (UM.Preferences.getValue("layerview/layer_view_type") == 3)
                         {
-                            return parseFloat(UM.SimulationView.getMaxThickness()).toFixed(2)
+                            return parseFloat(UM.SimulationView.maxThickness).toFixed(2)
                         }
                         //Line width selected
                         if(UM.Preferences.getValue("layerview/layer_view_type") == 4)
                         {
-                            return parseFloat(UM.SimulationView.getMaxLineWidth()).toFixed(2);
+                            return parseFloat(UM.SimulationView.maxLineWidth).toFixed(2);
                         }
                     }
                     return catalog.i18nc("@label","max")

+ 19 - 13
plugins/SimulationView/SimulationViewProxy.py

@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Ultimaker B.V.
+# Copyright (c) 2021 Ultimaker B.V.
 # Cura is released under the terms of the LGPLv3 or higher.
 from typing import TYPE_CHECKING
 
@@ -28,6 +28,7 @@ class SimulationViewProxy(QObject):
     globalStackChanged = pyqtSignal()
     preferencesChanged = pyqtSignal()
     busyChanged = pyqtSignal()
+    colorSchemeLimitsChanged = pyqtSignal()
 
     @pyqtProperty(bool, notify=activityChanged)
     def layerActivity(self):
@@ -101,28 +102,28 @@ class SimulationViewProxy(QObject):
     def getSimulationRunning(self):
         return self._simulation_view.isSimulationRunning()
 
-    @pyqtSlot(result=float)
-    def getMinFeedrate(self):
+    @pyqtProperty(float, notify = colorSchemeLimitsChanged)
+    def minFeedrate(self):
         return self._simulation_view.getMinFeedrate()
 
-    @pyqtSlot(result=float)
-    def getMaxFeedrate(self):
+    @pyqtProperty(float, notify = colorSchemeLimitsChanged)
+    def maxFeedrate(self):
         return self._simulation_view.getMaxFeedrate()
 
-    @pyqtSlot(result=float)
-    def getMinThickness(self):
+    @pyqtProperty(float, notify = colorSchemeLimitsChanged)
+    def minThickness(self):
         return self._simulation_view.getMinThickness()
 
-    @pyqtSlot(result=float)
-    def getMaxThickness(self):
+    @pyqtProperty(float, notify = colorSchemeLimitsChanged)
+    def maxThickness(self):
         return self._simulation_view.getMaxThickness()
 
-    @pyqtSlot(result=float)
-    def getMaxLineWidth(self):
+    @pyqtProperty(float, notify = colorSchemeLimitsChanged)
+    def maxLineWidth(self):
         return self._simulation_view.getMaxLineWidth()
 
-    @pyqtSlot(result=float)
-    def getMinLineWidth(self):
+    @pyqtProperty(float, notify = colorSchemeLimitsChanged)
+    def minLineWidth(self):
         return self._simulation_view.getMinLineWidth()
 
     # Opacity 0..1
@@ -153,6 +154,9 @@ class SimulationViewProxy(QObject):
         self.currentLayerChanged.emit()
         self._layerActivityChanged()
 
+    def _onColorSchemeLimitsChanged(self):
+        self.colorSchemeLimitsChanged.emit()
+
     def _onPathChanged(self):
         self.currentPathChanged.emit()
         self._layerActivityChanged()
@@ -182,6 +186,7 @@ class SimulationViewProxy(QObject):
         active_view = self._controller.getActiveView()
         if active_view == self._simulation_view:
             self._simulation_view.currentLayerNumChanged.connect(self._onLayerChanged)
+            self._simulation_view.colorSchemeLimitsChanged.connect(self._onColorSchemeLimitsChanged)
             self._simulation_view.currentPathNumChanged.connect(self._onPathChanged)
             self._simulation_view.maxLayersChanged.connect(self._onMaxLayersChanged)
             self._simulation_view.maxPathsChanged.connect(self._onMaxPathsChanged)
@@ -194,6 +199,7 @@ class SimulationViewProxy(QObject):
             # Disconnect all of em again.
             self.is_simulationView_selected = False
             self._simulation_view.currentLayerNumChanged.disconnect(self._onLayerChanged)
+            self._simulation_view.colorSchemeLimitsChanged.connect(self._onColorSchemeLimitsChanged)
             self._simulation_view.currentPathNumChanged.disconnect(self._onPathChanged)
             self._simulation_view.maxLayersChanged.disconnect(self._onMaxLayersChanged)
             self._simulation_view.maxPathsChanged.disconnect(self._onMaxPathsChanged)

+ 27 - 3
plugins/SimulationView/layers3d.shader

@@ -44,7 +44,15 @@ vertex41core =
 
     vec4 feedrateGradientColor(float abs_value, float min_value, float max_value)
     {
-        float value = (abs_value - min_value)/(max_value - min_value);
+        float value;
+        if(abs(max_value - min_value) < 0.0001) //Max and min are equal (barring floating point rounding errors).
+        {
+            value = 0.5; //Pick a colour in exactly the middle of the range.
+        }
+        else
+        {
+            value = (abs_value - min_value) / (max_value - min_value);
+        }
         float red = value;
         float green = 1-abs(1-4*value);
         if (value > 0.375)
@@ -57,7 +65,15 @@ vertex41core =
 
     vec4 layerThicknessGradientColor(float abs_value, float min_value, float max_value)
     {
-        float value = (abs_value - min_value)/(max_value - min_value);
+        float value;
+        if(abs(max_value - min_value) < 0.0001) //Max and min are equal (barring floating point rounding errors).
+        {
+            value = 0.5; //Pick a colour in exactly the middle of the range.
+        }
+        else
+        {
+            value = (abs_value - min_value) / (max_value - min_value);
+        }
         float red = min(max(4*value-2, 0), 1);
         float green = min(1.5*value, 0.75);
         if (value > 0.75)
@@ -70,7 +86,15 @@ vertex41core =
 
     vec4 lineWidthGradientColor(float abs_value, float min_value, float max_value)
     {
-        float value = (abs_value - min_value) / (max_value - min_value);
+        float value;
+        if(abs(max_value - min_value) < 0.0001) //Max and min are equal (barring floating point rounding errors).
+        {
+            value = 0.5; //Pick a colour in exactly the middle of the range.
+        }
+        else
+        {
+            value = (abs_value - min_value) / (max_value - min_value);
+        }
         float red = value;
         float green = 1 - abs(1 - 4 * value);
         if(value > 0.375)