|
@@ -40,7 +40,7 @@ from .SimulationViewProxy import SimulationViewProxy
|
|
|
import numpy
|
|
|
import os.path
|
|
|
|
|
|
-from typing import Optional, TYPE_CHECKING, List, cast
|
|
|
+from typing import Optional, TYPE_CHECKING, List, Tuple, cast
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
from UM.Scene.SceneNode import SceneNode
|
|
@@ -74,21 +74,20 @@ class SimulationView(CuraView):
|
|
|
self._old_max_layers = 0
|
|
|
|
|
|
self._max_paths = 0
|
|
|
- self._current_path_num = 0
|
|
|
+ self._current_path_num: float = 0.0
|
|
|
+ self._current_time = 0.0
|
|
|
self._minimum_path_num = 0
|
|
|
self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged)
|
|
|
|
|
|
- self._current_feedrates = {}
|
|
|
- self._lengths_of_polyline ={}
|
|
|
self._busy = False
|
|
|
self._simulation_running = False
|
|
|
|
|
|
- self._ghost_shader = None # type: Optional["ShaderProgram"]
|
|
|
- self._layer_pass = None # type: Optional[SimulationPass]
|
|
|
- self._composite_pass = None # type: Optional[CompositePass]
|
|
|
- self._old_layer_bindings = None # type: Optional[List[str]]
|
|
|
- self._simulationview_composite_shader = None # type: Optional["ShaderProgram"]
|
|
|
- self._old_composite_shader = None # type: Optional["ShaderProgram"]
|
|
|
+ self._ghost_shader: Optional["ShaderProgram"] = None
|
|
|
+ self._layer_pass: Optional[SimulationPass] = None
|
|
|
+ self._composite_pass: Optional[CompositePass] = None
|
|
|
+ self._old_layer_bindings: Optional[List[str]] = None
|
|
|
+ self._simulationview_composite_shader: Optional["ShaderProgram"] = None
|
|
|
+ self._old_composite_shader: Optional["ShaderProgram"] = None
|
|
|
|
|
|
self._max_feedrate = sys.float_info.min
|
|
|
self._min_feedrate = sys.float_info.max
|
|
@@ -99,13 +98,13 @@ class SimulationView(CuraView):
|
|
|
self._min_flow_rate = sys.float_info.max
|
|
|
self._max_flow_rate = sys.float_info.min
|
|
|
|
|
|
- self._global_container_stack = None # type: Optional[ContainerStack]
|
|
|
+ self._global_container_stack: Optional[ContainerStack] = None
|
|
|
self._proxy = None
|
|
|
|
|
|
self._resetSettings()
|
|
|
self._legend_items = None
|
|
|
self._show_travel_moves = False
|
|
|
- self._nozzle_node = None # type: Optional[NozzleNode]
|
|
|
+ self._nozzle_node: Optional[NozzleNode] = None
|
|
|
|
|
|
Application.getInstance().getPreferences().addPreference("view/top_layer_count", 5)
|
|
|
Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False)
|
|
@@ -127,13 +126,12 @@ class SimulationView(CuraView):
|
|
|
self._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers"))
|
|
|
self._compatibility_mode = self._evaluateCompatibilityMode()
|
|
|
|
|
|
- self._slice_first_warning_message = Message(catalog.i18nc("@info:status",
|
|
|
- "Nothing is shown because you need to slice first."),
|
|
|
- title = catalog.i18nc("@info:title", "No layers to show"),
|
|
|
- option_text = catalog.i18nc("@info:option_text",
|
|
|
- "Do not show this message again"),
|
|
|
- option_state = False,
|
|
|
- message_type = Message.MessageType.WARNING)
|
|
|
+ self._slice_first_warning_message = Message(catalog.i18nc("@info:status", "Nothing is shown because you need to slice first."),
|
|
|
+ title=catalog.i18nc("@info:title", "No layers to show"),
|
|
|
+ option_text=catalog.i18nc("@info:option_text",
|
|
|
+ "Do not show this message again"),
|
|
|
+ option_state=False,
|
|
|
+ message_type=Message.MessageType.WARNING)
|
|
|
self._slice_first_warning_message.optionToggled.connect(self._onDontAskMeAgain)
|
|
|
CuraApplication.getInstance().getPreferences().addPreference(self._no_layers_warning_preference, True)
|
|
|
|
|
@@ -189,9 +187,82 @@ class SimulationView(CuraView):
|
|
|
def getMaxLayers(self) -> int:
|
|
|
return self._max_layers
|
|
|
|
|
|
- def getCurrentPath(self) -> int:
|
|
|
+ def getCurrentPath(self) -> float:
|
|
|
return self._current_path_num
|
|
|
|
|
|
+ def setTime(self, time: float) -> None:
|
|
|
+ self._current_time = time
|
|
|
+
|
|
|
+ left_i = 0
|
|
|
+ right_i = self._max_paths - 1
|
|
|
+
|
|
|
+ total_duration, cumulative_line_duration = self.cumulativeLineDuration()
|
|
|
+
|
|
|
+ # make an educated guess about where to start
|
|
|
+ i = int(right_i * max(0.0, min(1.0, self._current_time / total_duration)))
|
|
|
+
|
|
|
+ # binary search for the correct path
|
|
|
+ while left_i < right_i:
|
|
|
+ if cumulative_line_duration[i] <= self._current_time:
|
|
|
+ left_i = i + 1
|
|
|
+ else:
|
|
|
+ right_i = i
|
|
|
+ i = int((left_i + right_i) / 2)
|
|
|
+
|
|
|
+ left_value = cumulative_line_duration[i - 1] if i > 0 else 0.0
|
|
|
+ right_value = cumulative_line_duration[i]
|
|
|
+
|
|
|
+ assert (left_value <= self._current_time <= right_value)
|
|
|
+
|
|
|
+ fractional_value = (self._current_time - left_value) / (right_value - left_value)
|
|
|
+
|
|
|
+ self.setPath(i + fractional_value)
|
|
|
+
|
|
|
+ def advanceTime(self, time_increase: float) -> bool:
|
|
|
+ """
|
|
|
+ Advance the time by the given amount.
|
|
|
+
|
|
|
+ :param time_increase: The amount of time to advance (in seconds).
|
|
|
+ :return: True if the time was advanced, False if the end of the simulation was reached.
|
|
|
+ """
|
|
|
+ total_duration, cumulative_line_duration = self.cumulativeLineDuration()
|
|
|
+
|
|
|
+ # time ratio
|
|
|
+ time_increase = time_increase
|
|
|
+
|
|
|
+ if self._current_time + time_increase > total_duration:
|
|
|
+ # If we have reached the end of the simulation, go to the next layer.
|
|
|
+ if self.getCurrentLayer() == self.getMaxLayers():
|
|
|
+ # If we are already at the last layer, go to the first layer.
|
|
|
+ self.setTime(total_duration)
|
|
|
+ return False
|
|
|
+
|
|
|
+ # advance to the next layer, and reset the time
|
|
|
+ self.setLayer(self.getCurrentLayer() + 1)
|
|
|
+ self.setTime(0.0)
|
|
|
+ else:
|
|
|
+ self.setTime(self._current_time + time_increase)
|
|
|
+ return True
|
|
|
+
|
|
|
+ def cumulativeLineDuration(self) -> Tuple[float, List[float]]:
|
|
|
+ # TODO: cache the total duration and cumulative line duration at each layer change event
|
|
|
+ cumulative_line_duration = []
|
|
|
+ total_duration = 0.0
|
|
|
+ for polyline in self.getLayerData().polygons:
|
|
|
+ for line_duration in list((polyline.lineLengths / polyline.lineFeedrates)[0]):
|
|
|
+ total_duration += line_duration
|
|
|
+ cumulative_line_duration.append(total_duration)
|
|
|
+ return total_duration, cumulative_line_duration
|
|
|
+
|
|
|
+ def getLayerData(self) -> Optional["LayerData"]:
|
|
|
+ scene = self.getController().getScene()
|
|
|
+ for node in DepthFirstIterator(scene.getRoot()): # type: ignore
|
|
|
+ layer_data = node.callDecoration("getLayerData")
|
|
|
+ if not layer_data:
|
|
|
+ continue
|
|
|
+ return layer_data.getLayer(self.getCurrentLayer())
|
|
|
+ return None
|
|
|
+
|
|
|
def getMinimumPath(self) -> int:
|
|
|
return self._minimum_path_num
|
|
|
|
|
@@ -279,7 +350,7 @@ class SimulationView(CuraView):
|
|
|
self._startUpdateTopLayers()
|
|
|
self.currentLayerNumChanged.emit()
|
|
|
|
|
|
- def setPath(self, value: int) -> None:
|
|
|
+ def setPath(self, value: float) -> None:
|
|
|
"""
|
|
|
Set the upper end of the range of visible paths on the current layer.
|
|
|
|
|
@@ -402,15 +473,6 @@ class SimulationView(CuraView):
|
|
|
def getMaxFeedrate(self) -> float:
|
|
|
return self._max_feedrate
|
|
|
|
|
|
- def getSimulationTime(self, currentIndex) -> float:
|
|
|
- try:
|
|
|
- return (self._lengths_of_polyline[self._current_layer_num][currentIndex] / self._current_feedrates[self._current_layer_num][currentIndex])[0]
|
|
|
-
|
|
|
- except:
|
|
|
- # In case of change in layers, currentIndex comes one more than the items in the lengths_of_polyline
|
|
|
- # We give 1 second time for layer change
|
|
|
- return 1.0
|
|
|
-
|
|
|
def getMinThickness(self) -> float:
|
|
|
if abs(self._min_thickness - 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.
|
|
@@ -535,10 +597,8 @@ class SimulationView(CuraView):
|
|
|
visible_indicies_with_extrusion = numpy.where(numpy.isin(polyline.types, visible_line_types_with_extrusion))[0]
|
|
|
if visible_indices.size == 0: # No items to take maximum or minimum of.
|
|
|
continue
|
|
|
- self._lengths_of_polyline[layer_index] = polyline.lineLengths
|
|
|
visible_feedrates = numpy.take(polyline.lineFeedrates, visible_indices)
|
|
|
visible_feedrates_with_extrusion = numpy.take(polyline.lineFeedrates, visible_indicies_with_extrusion)
|
|
|
- self._current_feedrates[layer_index] = polyline.lineFeedrates
|
|
|
visible_linewidths = numpy.take(polyline.lineWidths, visible_indices)
|
|
|
visible_linewidths_with_extrusion = numpy.take(polyline.lineWidths, visible_indicies_with_extrusion)
|
|
|
visible_thicknesses = numpy.take(polyline.lineThicknesses, visible_indices)
|