mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
Proof of concept for simulation
Co-authored-by: Casper Lamboo <c.lamboo@ultimaker.com> CURA-7647
This commit is contained in:
parent
3c550557b9
commit
cfec5e0cc1
5 changed files with 128 additions and 104 deletions
|
@ -67,7 +67,7 @@ class LayerPolygon:
|
||||||
# Buffering the colors shouldn't be necessary as it is not
|
# Buffering the colors shouldn't be necessary as it is not
|
||||||
# re-used and can save a lot of memory usage.
|
# re-used and can save a lot of memory usage.
|
||||||
self._color_map = LayerPolygon.getColorMap()
|
self._color_map = LayerPolygon.getColorMap()
|
||||||
self._colors = self._color_map[self._types] # type: numpy.ndarray
|
self._colors: numpy.ndarray = self._color_map[self._types]
|
||||||
|
|
||||||
# When type is used as index returns true if type == LayerPolygon.InfillType
|
# When type is used as index returns true if type == LayerPolygon.InfillType
|
||||||
# or type == LayerPolygon.SkinType
|
# or type == LayerPolygon.SkinType
|
||||||
|
@ -75,8 +75,8 @@ class LayerPolygon:
|
||||||
# Should be generated in better way, not hardcoded.
|
# Should be generated in better way, not hardcoded.
|
||||||
self._is_infill_or_skin_type_map = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0], dtype=bool)
|
self._is_infill_or_skin_type_map = numpy.array([0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0], dtype=bool)
|
||||||
|
|
||||||
self._build_cache_line_mesh_mask = None # type: Optional[numpy.ndarray]
|
self._build_cache_line_mesh_mask: Optional[numpy.ndarray] = None
|
||||||
self._build_cache_needed_points = None # type: Optional[numpy.ndarray]
|
self._build_cache_needed_points: Optional[numpy.ndarray] = None
|
||||||
|
|
||||||
def buildCache(self) -> None:
|
def buildCache(self) -> None:
|
||||||
# For the line mesh we do not draw Infill or Jumps. Therefore those lines are filtered out.
|
# For the line mesh we do not draw Infill or Jumps. Therefore those lines are filtered out.
|
||||||
|
|
|
@ -35,7 +35,7 @@ class SimulationPass(RenderPass):
|
||||||
self._nozzle_shader = None
|
self._nozzle_shader = None
|
||||||
self._disabled_shader = None
|
self._disabled_shader = None
|
||||||
self._old_current_layer = 0
|
self._old_current_layer = 0
|
||||||
self._old_current_path = 0
|
self._old_current_path: float = 0.0
|
||||||
self._switching_layers = True # Tracking whether the user is moving across layers (True) or across paths (False). If false, lower layers render as shadowy.
|
self._switching_layers = True # Tracking whether the user is moving across layers (True) or across paths (False). If false, lower layers render as shadowy.
|
||||||
self._gl = OpenGL.getInstance().getBindingsObject()
|
self._gl = OpenGL.getInstance().getBindingsObject()
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
|
@ -139,7 +139,7 @@ class SimulationPass(RenderPass):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Render all layers below a certain number as line mesh instead of vertices.
|
# Render all layers below a certain number as line mesh instead of vertices.
|
||||||
if self._layer_view._current_layer_num > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
|
if self._layer_view.getCurrentLayer() > -1 and ((not self._layer_view._only_show_top_layers) or (not self._layer_view.getCompatibilityMode())):
|
||||||
start = 0
|
start = 0
|
||||||
end = 0
|
end = 0
|
||||||
element_counts = layer_data.getElementCounts()
|
element_counts = layer_data.getElementCounts()
|
||||||
|
@ -147,7 +147,7 @@ class SimulationPass(RenderPass):
|
||||||
# In the current layer, we show just the indicated paths
|
# In the current layer, we show just the indicated paths
|
||||||
if layer == self._layer_view._current_layer_num:
|
if layer == self._layer_view._current_layer_num:
|
||||||
# We look for the position of the head, searching the point of the current path
|
# We look for the position of the head, searching the point of the current path
|
||||||
index = self._layer_view._current_path_num
|
index = int(self._layer_view.getCurrentPath())
|
||||||
offset = 0
|
offset = 0
|
||||||
for polygon in layer_data.getLayer(layer).polygons:
|
for polygon in layer_data.getLayer(layer).polygons:
|
||||||
# The size indicates all values in the two-dimension array, and the second dimension is
|
# The size indicates all values in the two-dimension array, and the second dimension is
|
||||||
|
@ -157,23 +157,33 @@ class SimulationPass(RenderPass):
|
||||||
offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
|
offset = 1 # This is to avoid the first point when there is more than one polygon, since has the same value as the last point in the previous polygon
|
||||||
continue
|
continue
|
||||||
# The head position is calculated and translated
|
# The head position is calculated and translated
|
||||||
head_position = Vector(polygon.data[index+offset][0], polygon.data[index+offset][1], polygon.data[index+offset][2]) + node.getWorldPosition()
|
ratio = self._layer_view.getCurrentPath() - index
|
||||||
|
pos_a = Vector(polygon.data[index + offset][0], polygon.data[index + offset][1],
|
||||||
|
polygon.data[index + offset][2])
|
||||||
|
if ratio > 0.0001:
|
||||||
|
pos_b = Vector(polygon.data[index + offset + 1][0],
|
||||||
|
polygon.data[index + offset + 1][1],
|
||||||
|
polygon.data[index + offset + 1][2])
|
||||||
|
vec = pos_a * (1.0 - ratio) + pos_b * ratio
|
||||||
|
head_position = vec + node.getWorldPosition()
|
||||||
|
else:
|
||||||
|
head_position = pos_a + node.getWorldPosition()
|
||||||
break
|
break
|
||||||
break
|
break
|
||||||
if self._layer_view._minimum_layer_num > layer:
|
if self._layer_view.getMinimumLayer() > layer:
|
||||||
start += element_counts[layer]
|
start += element_counts[layer]
|
||||||
end += element_counts[layer]
|
end += element_counts[layer]
|
||||||
|
|
||||||
# Calculate the range of paths in the last layer
|
# Calculate the range of paths in the last layer
|
||||||
current_layer_start = end
|
current_layer_start = end
|
||||||
current_layer_end = end + self._layer_view._current_path_num * 2 # Because each point is used twice
|
current_layer_end = end + int( self._layer_view.getCurrentPath()) * 2 # Because each point is used twice
|
||||||
|
|
||||||
# This uses glDrawRangeElements internally to only draw a certain range of lines.
|
# This uses glDrawRangeElements internally to only draw a certain range of lines.
|
||||||
# All the layers but the current selected layer are rendered first
|
# All the layers but the current selected layer are rendered first
|
||||||
if self._old_current_path != self._layer_view._current_path_num:
|
if self._old_current_path != self._layer_view.getCurrentPath():
|
||||||
self._current_shader = self._layer_shadow_shader
|
self._current_shader = self._layer_shadow_shader
|
||||||
self._switching_layers = False
|
self._switching_layers = False
|
||||||
if not self._layer_view.isSimulationRunning() and self._old_current_layer != self._layer_view._current_layer_num:
|
if not self._layer_view.isSimulationRunning() and self._old_current_layer != self._layer_view.getCurrentLayer():
|
||||||
self._current_shader = self._layer_shader
|
self._current_shader = self._layer_shader
|
||||||
self._switching_layers = True
|
self._switching_layers = True
|
||||||
|
|
||||||
|
@ -193,8 +203,8 @@ class SimulationPass(RenderPass):
|
||||||
current_layer_batch.addItem(node.getWorldTransformation(), layer_data)
|
current_layer_batch.addItem(node.getWorldTransformation(), layer_data)
|
||||||
current_layer_batch.render(self._scene.getActiveCamera())
|
current_layer_batch.render(self._scene.getActiveCamera())
|
||||||
|
|
||||||
self._old_current_layer = self._layer_view._current_layer_num
|
self._old_current_layer = self._layer_view.getCurrentLayer()
|
||||||
self._old_current_path = self._layer_view._current_path_num
|
self._old_current_path = self._layer_view.getCurrentPath()
|
||||||
|
|
||||||
# Create a new batch that is not range-limited
|
# Create a new batch that is not range-limited
|
||||||
batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid)
|
batch = RenderBatch(self._layer_shader, type = RenderBatch.RenderType.Solid)
|
||||||
|
@ -230,4 +240,4 @@ class SimulationPass(RenderPass):
|
||||||
if changed_object.callDecoration("getLayerData"): # Any layer data has changed.
|
if changed_object.callDecoration("getLayerData"): # Any layer data has changed.
|
||||||
self._switching_layers = True
|
self._switching_layers = True
|
||||||
self._old_current_layer = 0
|
self._old_current_layer = 0
|
||||||
self._old_current_path = 0
|
self._old_current_path = 0.0
|
||||||
|
|
|
@ -40,7 +40,7 @@ from .SimulationViewProxy import SimulationViewProxy
|
||||||
import numpy
|
import numpy
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
from typing import Optional, TYPE_CHECKING, List, cast
|
from typing import Optional, TYPE_CHECKING, List, Tuple, cast
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from UM.Scene.SceneNode import SceneNode
|
from UM.Scene.SceneNode import SceneNode
|
||||||
|
@ -74,21 +74,20 @@ class SimulationView(CuraView):
|
||||||
self._old_max_layers = 0
|
self._old_max_layers = 0
|
||||||
|
|
||||||
self._max_paths = 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._minimum_path_num = 0
|
||||||
self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged)
|
self.currentLayerNumChanged.connect(self._onCurrentLayerNumChanged)
|
||||||
|
|
||||||
self._current_feedrates = {}
|
|
||||||
self._lengths_of_polyline ={}
|
|
||||||
self._busy = False
|
self._busy = False
|
||||||
self._simulation_running = False
|
self._simulation_running = False
|
||||||
|
|
||||||
self._ghost_shader = None # type: Optional["ShaderProgram"]
|
self._ghost_shader: Optional["ShaderProgram"] = None
|
||||||
self._layer_pass = None # type: Optional[SimulationPass]
|
self._layer_pass: Optional[SimulationPass] = None
|
||||||
self._composite_pass = None # type: Optional[CompositePass]
|
self._composite_pass: Optional[CompositePass] = None
|
||||||
self._old_layer_bindings = None # type: Optional[List[str]]
|
self._old_layer_bindings: Optional[List[str]] = None
|
||||||
self._simulationview_composite_shader = None # type: Optional["ShaderProgram"]
|
self._simulationview_composite_shader: Optional["ShaderProgram"] = None
|
||||||
self._old_composite_shader = None # type: Optional["ShaderProgram"]
|
self._old_composite_shader: Optional["ShaderProgram"] = None
|
||||||
|
|
||||||
self._max_feedrate = sys.float_info.min
|
self._max_feedrate = sys.float_info.min
|
||||||
self._min_feedrate = sys.float_info.max
|
self._min_feedrate = sys.float_info.max
|
||||||
|
@ -99,13 +98,13 @@ class SimulationView(CuraView):
|
||||||
self._min_flow_rate = sys.float_info.max
|
self._min_flow_rate = sys.float_info.max
|
||||||
self._max_flow_rate = sys.float_info.min
|
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._proxy = None
|
||||||
|
|
||||||
self._resetSettings()
|
self._resetSettings()
|
||||||
self._legend_items = None
|
self._legend_items = None
|
||||||
self._show_travel_moves = False
|
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/top_layer_count", 5)
|
||||||
Application.getInstance().getPreferences().addPreference("view/only_show_top_layers", False)
|
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._only_show_top_layers = bool(Application.getInstance().getPreferences().getValue("view/only_show_top_layers"))
|
||||||
self._compatibility_mode = self._evaluateCompatibilityMode()
|
self._compatibility_mode = self._evaluateCompatibilityMode()
|
||||||
|
|
||||||
self._slice_first_warning_message = Message(catalog.i18nc("@info:status",
|
self._slice_first_warning_message = Message(catalog.i18nc("@info:status", "Nothing is shown because you need to slice first."),
|
||||||
"Nothing is shown because you need to slice first."),
|
title=catalog.i18nc("@info:title", "No layers to show"),
|
||||||
title = catalog.i18nc("@info:title", "No layers to show"),
|
option_text=catalog.i18nc("@info:option_text",
|
||||||
option_text = catalog.i18nc("@info:option_text",
|
"Do not show this message again"),
|
||||||
"Do not show this message again"),
|
option_state=False,
|
||||||
option_state = False,
|
message_type=Message.MessageType.WARNING)
|
||||||
message_type = Message.MessageType.WARNING)
|
|
||||||
self._slice_first_warning_message.optionToggled.connect(self._onDontAskMeAgain)
|
self._slice_first_warning_message.optionToggled.connect(self._onDontAskMeAgain)
|
||||||
CuraApplication.getInstance().getPreferences().addPreference(self._no_layers_warning_preference, True)
|
CuraApplication.getInstance().getPreferences().addPreference(self._no_layers_warning_preference, True)
|
||||||
|
|
||||||
|
@ -189,9 +187,82 @@ class SimulationView(CuraView):
|
||||||
def getMaxLayers(self) -> int:
|
def getMaxLayers(self) -> int:
|
||||||
return self._max_layers
|
return self._max_layers
|
||||||
|
|
||||||
def getCurrentPath(self) -> int:
|
def getCurrentPath(self) -> float:
|
||||||
return self._current_path_num
|
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:
|
def getMinimumPath(self) -> int:
|
||||||
return self._minimum_path_num
|
return self._minimum_path_num
|
||||||
|
|
||||||
|
@ -279,7 +350,7 @@ class SimulationView(CuraView):
|
||||||
self._startUpdateTopLayers()
|
self._startUpdateTopLayers()
|
||||||
self.currentLayerNumChanged.emit()
|
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.
|
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:
|
def getMaxFeedrate(self) -> float:
|
||||||
return self._max_feedrate
|
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:
|
def getMinThickness(self) -> float:
|
||||||
if abs(self._min_thickness - sys.float_info.max) < 10: # Some lenience due to floating point rounding.
|
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.
|
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]
|
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.
|
if visible_indices.size == 0: # No items to take maximum or minimum of.
|
||||||
continue
|
continue
|
||||||
self._lengths_of_polyline[layer_index] = polyline.lineLengths
|
|
||||||
visible_feedrates = numpy.take(polyline.lineFeedrates, visible_indices)
|
visible_feedrates = numpy.take(polyline.lineFeedrates, visible_indices)
|
||||||
visible_feedrates_with_extrusion = numpy.take(polyline.lineFeedrates, visible_indicies_with_extrusion)
|
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 = numpy.take(polyline.lineWidths, visible_indices)
|
||||||
visible_linewidths_with_extrusion = numpy.take(polyline.lineWidths, visible_indicies_with_extrusion)
|
visible_linewidths_with_extrusion = numpy.take(polyline.lineWidths, visible_indicies_with_extrusion)
|
||||||
visible_thicknesses = numpy.take(polyline.lineThicknesses, visible_indices)
|
visible_thicknesses = numpy.take(polyline.lineThicknesses, visible_indices)
|
||||||
|
|
|
@ -136,54 +136,19 @@ Item
|
||||||
Timer
|
Timer
|
||||||
{
|
{
|
||||||
id: simulationTimer
|
id: simulationTimer
|
||||||
interval: UM.SimulationView.simulationTime
|
interval: 1000 / 60
|
||||||
running: false
|
running: false
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered:
|
onTriggered:
|
||||||
{
|
{
|
||||||
var currentPath = UM.SimulationView.currentPath
|
// divide by 1000 to accont for ms to s conversion
|
||||||
var numPaths = UM.SimulationView.numPaths
|
const advance_time = simulationTimer.interval / 1000.0;
|
||||||
var currentLayer = UM.SimulationView.currentLayer
|
if (!UM.SimulationView.advanceTime(advance_time)) {
|
||||||
var numLayers = UM.SimulationView.numLayers
|
playButton.pauseSimulation();
|
||||||
|
|
||||||
// When the user plays the simulation, if the path slider is at the end of this layer, we start
|
|
||||||
// the simulation at the beginning of the current layer.
|
|
||||||
if (!isSimulationPlaying)
|
|
||||||
{
|
|
||||||
if (currentPath >= numPaths)
|
|
||||||
{
|
|
||||||
UM.SimulationView.setCurrentPath(0)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UM.SimulationView.setCurrentPath(currentPath + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the simulation is already playing and we reach the end of a layer, then it automatically
|
|
||||||
// starts at the beginning of the next layer.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (currentPath >= numPaths)
|
|
||||||
{
|
|
||||||
// At the end of the model, the simulation stops
|
|
||||||
if (currentLayer >= numLayers)
|
|
||||||
{
|
|
||||||
playButton.pauseSimulation()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UM.SimulationView.setCurrentLayer(currentLayer + 1)
|
|
||||||
UM.SimulationView.setCurrentPath(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UM.SimulationView.setCurrentPath(currentPath + 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// The status must be set here instead of in the resumeSimulation function otherwise it won't work
|
// The status must be set here instead of in the resumeSimulation function otherwise it won't work
|
||||||
// correctly, because part of the logic is in this trigger function.
|
// correctly, because part of the logic is in this trigger function.
|
||||||
isSimulationPlaying = true
|
isSimulationPlaying = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
# Cura is released under the terms of the LGPLv3 or higher.
|
# Cura is released under the terms of the LGPLv3 or higher.
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
import numpy
|
|
||||||
from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty
|
from PyQt6.QtCore import QObject, pyqtSignal, pyqtProperty
|
||||||
from UM.FlameProfiler import pyqtSlot
|
from UM.FlameProfiler import pyqtSlot
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
|
@ -12,11 +11,6 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
|
|
||||||
class SimulationViewProxy(QObject):
|
class SimulationViewProxy(QObject):
|
||||||
|
|
||||||
S_TO_MS = 1000
|
|
||||||
SPEED_OF_SIMULATION = 10
|
|
||||||
FACTOR = S_TO_MS/SPEED_OF_SIMULATION
|
|
||||||
|
|
||||||
def __init__(self, simulation_view: "SimulationView", parent=None) -> None:
|
def __init__(self, simulation_view: "SimulationView", parent=None) -> None:
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._simulation_view = simulation_view
|
self._simulation_view = simulation_view
|
||||||
|
@ -56,17 +50,13 @@ class SimulationViewProxy(QObject):
|
||||||
def numPaths(self):
|
def numPaths(self):
|
||||||
return self._simulation_view.getMaxPaths()
|
return self._simulation_view.getMaxPaths()
|
||||||
|
|
||||||
@pyqtProperty(int, notify=currentPathChanged)
|
@pyqtProperty(float, notify=currentPathChanged)
|
||||||
def currentPath(self):
|
def currentPath(self):
|
||||||
return self._simulation_view.getCurrentPath()
|
return self._simulation_view.getCurrentPath()
|
||||||
|
|
||||||
@pyqtProperty(int, notify=currentPathChanged)
|
@pyqtSlot(float, result=bool)
|
||||||
def simulationTime(self):
|
def advanceTime(self, duration: float) -> bool:
|
||||||
# Extracts the currents paths simulation time (in seconds) for the current path from the dict of simulation time of the current layer.
|
return self._simulation_view.advanceTime(duration)
|
||||||
# We multiply the time with 100 to make it to ms from s.(Should be 1000 in real time). This scaling makes the simulation time 10x faster than the real time.
|
|
||||||
simulationTimeOfpath = self._simulation_view.getSimulationTime(self._simulation_view.getCurrentPath()) * SimulationViewProxy.FACTOR
|
|
||||||
# Since the timer cannot process time less than 1 ms, we put a lower limit here
|
|
||||||
return int(max(1, simulationTimeOfpath))
|
|
||||||
|
|
||||||
@pyqtProperty(int, notify=currentPathChanged)
|
@pyqtProperty(int, notify=currentPathChanged)
|
||||||
def minimumPath(self):
|
def minimumPath(self):
|
||||||
|
@ -92,8 +82,8 @@ class SimulationViewProxy(QObject):
|
||||||
def setMinimumLayer(self, layer_num):
|
def setMinimumLayer(self, layer_num):
|
||||||
self._simulation_view.setMinimumLayer(layer_num)
|
self._simulation_view.setMinimumLayer(layer_num)
|
||||||
|
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(float)
|
||||||
def setCurrentPath(self, path_num):
|
def setCurrentPath(self, path_num: float):
|
||||||
self._simulation_view.setPath(path_num)
|
self._simulation_view.setPath(path_num)
|
||||||
|
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
|
@ -229,4 +219,3 @@ class SimulationViewProxy(QObject):
|
||||||
self._simulation_view.activityChanged.disconnect(self._onActivityChanged)
|
self._simulation_view.activityChanged.disconnect(self._onActivityChanged)
|
||||||
self._simulation_view.globalStackChanged.disconnect(self._onGlobalStackChanged)
|
self._simulation_view.globalStackChanged.disconnect(self._onGlobalStackChanged)
|
||||||
self._simulation_view.preferencesChanged.disconnect(self._onPreferencesChanged)
|
self._simulation_view.preferencesChanged.disconnect(self._onPreferencesChanged)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue