CURA-4525 first multi slice + multi layer data, added filter on build plate, added option arrange on load, visuals like convex hull are now correct

This commit is contained in:
Jack Ha 2017-11-09 17:03:20 +01:00
parent 41d5ec86a3
commit e21acd1a07
18 changed files with 468 additions and 260 deletions

View file

@ -40,7 +40,7 @@ class Arrange:
# \param fixed_nodes Scene nodes to be placed
@classmethod
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 220, y = 220):
arranger = Arrange(x, y, x / 2, y / 2, scale = scale)
arranger = Arrange(x, y, x // 2, y // 2, scale = scale)
arranger.centerFirst()
if fixed_nodes is None:

View file

@ -112,7 +112,6 @@ class ArrangeObjectsAllBuildPlatesJob(Job):
# start_priority = 0
while try_placement:
Logger.log("d", "start_priority %s", start_priority)
# make sure that current_build_plate_number is not going crazy or you'll have a lot of arrange objects
while current_build_plate_number >= arrange_array.count():
arrange_array.add()

View file

@ -6,7 +6,6 @@ from UM.Scene.SceneNode import SceneNode
from UM.Resources import Resources
from UM.Math.Color import Color
from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with.
from UM.View.GL.OpenGL import OpenGL
@ -65,7 +64,7 @@ class ConvexHullNode(SceneNode):
ConvexHullNode.shader.setUniformValue("u_diffuseColor", self._color)
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
if self.getParent():
if self.getParent() and self.getParent().callDecoration("getBuildPlateNumber") == Application.getInstance().activeBuildPlate:
if self.getMeshData():
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
if self._convex_hull_head_mesh:

View file

@ -33,6 +33,7 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.GroupedOperation import GroupedOperation
from UM.Operations.SetTransformOperation import SetTransformOperation
from cura.Arrange import Arrange
from cura.ShapeArray import ShapeArray
from cura.ConvexHullDecorator import ConvexHullDecorator
@ -41,6 +42,7 @@ from cura.SliceableObjectDecorator import SliceableObjectDecorator
from cura.BlockSlicingDecorator import BlockSlicingDecorator
# research
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from cura.Scene.CuraSceneNode import CuraSceneNode
from cura.ArrangeObjectsJob import ArrangeObjectsJob
from cura.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
@ -307,11 +309,13 @@ class CuraApplication(QtApplication):
preferences.addPreference("cura/asked_dialog_on_project_save", False)
preferences.addPreference("cura/choice_on_profile_override", "always_ask")
preferences.addPreference("cura/choice_on_open_project", "always_ask")
preferences.addPreference("cura/arrange_objects_on_load", True)
preferences.addPreference("cura/currency", "")
preferences.addPreference("cura/material_settings", "{}")
preferences.addPreference("view/invert_zoom", False)
preferences.addPreference("view/filter_current_build_plate", False)
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement")
@ -896,7 +900,7 @@ class CuraApplication(QtApplication):
scene_bounding_box = None
is_block_slicing_node = False
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode or (not node.getMeshData() and not node.callDecoration("getLayerData")):
if not issubclass(type(node), SceneNode) or (not node.getMeshData() and not node.callDecoration("getLayerData")):
continue
if node.callDecoration("isBlockSlicing"):
is_block_slicing_node = True
@ -1013,7 +1017,7 @@ class CuraApplication(QtApplication):
Selection.clear()
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not issubclass(type(node), SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1021,6 +1025,9 @@ class CuraApplication(QtApplication):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data
if not node.callDecoration("isSliceable"):
continue # i.e. node with layer data
Selection.add(node)
## Delete all nodes containing mesh data in the scene.
@ -1032,7 +1039,7 @@ class CuraApplication(QtApplication):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not issubclass(type(node), SceneNode):
continue
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1054,7 +1061,7 @@ class CuraApplication(QtApplication):
Logger.log("i", "Resetting all scene translations")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not issubclass(type(node), SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1082,13 +1089,13 @@ class CuraApplication(QtApplication):
Logger.log("i", "Resetting all scene transformations")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not issubclass(type(node), SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
if not node.callDecoration("isSliceable"):
continue # i.e. node with layer data
nodes.append(node)
@ -1109,7 +1116,27 @@ class CuraApplication(QtApplication):
def arrangeObjectsToAllBuildPlates(self):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not issubclass(type(node), SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.callDecoration("isSliceable"):
continue # i.e. node with layer data
# Skip nodes that are too big
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
nodes.append(node)
job = ArrangeObjectsAllBuildPlatesJob(nodes)
job.start()
self.setActiveBuildPlate(0)
# Single build plate
@pyqtSlot()
def arrangeAll(self):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if not issubclass(type(node), SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1117,24 +1144,7 @@ class CuraApplication(QtApplication):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data
# Skip nodes that are too big
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
nodes.append(node)
job = ArrangeObjectsAllBuildPlatesJob(nodes)
job.start()
# Single build plate
@pyqtSlot()
def arrangeAll(self):
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
if node.getParent() and node.getParent().callDecoration("isGroup"):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
if not node.callDecoration("isSliceable"):
continue # i.e. node with layer data
if node.callDecoration("getBuildPlateNumber") == self._active_build_plate:
# Skip nodes that are too big
@ -1150,7 +1160,7 @@ class CuraApplication(QtApplication):
# What nodes are on the build plate and are not being moved
fixed_nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode:
if not issubclass(type(node), SceneNode):
continue
if not node.getMeshData() and not node.callDecoration("isGroup"):
continue # Node that doesnt have a mesh and is not a group.
@ -1158,6 +1168,8 @@ class CuraApplication(QtApplication):
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
if not node.isSelectable():
continue # i.e. node with layer data
if not node.callDecoration("isSliceable"):
continue # i.e. node with layer data
if node in nodes: # exclude selected node from fixed_nodes
continue
fixed_nodes.append(node)
@ -1176,7 +1188,7 @@ class CuraApplication(QtApplication):
Logger.log("i", "Reloading all loaded mesh data.")
nodes = []
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
if type(node) is not SceneNode or not node.getMeshData():
if not issubclass(type(node), SceneNode) or not node.getMeshData():
continue
nodes.append(node)
@ -1267,7 +1279,7 @@ class CuraApplication(QtApplication):
@pyqtSlot()
def groupSelected(self):
# Create a group-node
group_node = SceneNode()
group_node = CuraSceneNode()
group_decorator = GroupDecorator()
group_node.addDecorator(group_decorator)
group_node.addDecorator(ConvexHullDecorator())
@ -1413,11 +1425,15 @@ class CuraApplication(QtApplication):
min_offset = 8
self.fileLoaded.emit(filename)
arrange_objects_on_load = Preferences.getInstance().getValue("cura/arrange_objects_on_load")
target_build_plate = self.activeBuildPlate if arrange_objects_on_load else -1
for original_node in nodes:
node = CuraSceneNode() # We want our own CuraSceneNode
node.setMeshData(original_node.getMeshData())
for node in nodes:
node.setSelectable(True)
node.setName(os.path.basename(filename))
node.addDecorator(BuildPlateDecorator())
extension = os.path.splitext(filename)[1]
if extension.lower() in self._non_sliceable_extensions:
@ -1442,6 +1458,7 @@ class CuraApplication(QtApplication):
if not child.getDecorator(ConvexHullDecorator):
child.addDecorator(ConvexHullDecorator())
if arrange_objects_on_load:
if node.callDecoration("isSliceable"):
# Only check position if it's not already blatantly obvious that it won't fit.
if node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
@ -1457,6 +1474,8 @@ class CuraApplication(QtApplication):
# Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher
node, _ = arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10)
node.addDecorator(BuildPlateDecorator(target_build_plate))
op = AddSceneNodeOperation(node, scene.getRoot())
op.push()
scene.sceneChanged.emit(node)
@ -1494,6 +1513,8 @@ class CuraApplication(QtApplication):
#### research - hacky place for these kind of thing
@pyqtSlot(int)
def setActiveBuildPlate(self, nr):
if nr == self._active_build_plate:
return
Logger.log("d", "Select build plate: %s" % nr)
self._active_build_plate = nr

View file

@ -7,23 +7,35 @@ from UM.Scene.SceneNode import SceneNode
from UM.Scene.Selection import Selection
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication
#from cura.Scene.CuraSceneNode import CuraSceneNode
from UM.Preferences import Preferences
class ObjectManager(ListModel):
def __init__(self):
super().__init__()
self._last_selected_index = 0
Application.getInstance().getController().getScene().sceneChanged.connect(self._update)
Application.getInstance().getController().getScene().sceneChanged.connect(self._update_scene_changed)
Preferences.getInstance().preferenceChanged.connect(self._update)
Application.getInstance().activeBuildPlateChanged.connect(self._update)
def _update(self, *args):
nodes = []
filter_current_build_plate = Preferences.getInstance().getValue("view/filter_current_build_plate")
active_build_plate_number = Application.getInstance().activeBuildPlate
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
if type(node) is not SceneNode or (not node.getMeshData() and not node.callDecoration("getLayerData")):
if not issubclass(type(node), SceneNode) or (not node.getMeshData() and not node.callDecoration("getLayerData")):
continue
if not node.callDecoration("isSliceable"):
continue
node_build_plate_number = node.callDecoration("getBuildPlateNumber")
if filter_current_build_plate and node_build_plate_number != active_build_plate_number:
continue
nodes.append({
"name": node.getName(),
"isSelected": Selection.isSelected(node),
"buildPlateNumber": node.callDecoration("getBuildPlateNumber"),
"isOutsideBuildArea": node.isOutsideBuildArea(),
"buildPlateNumber": node_build_plate_number,
"node": node
})
nodes = sorted(nodes, key=lambda n: n["name"])
@ -31,6 +43,12 @@ class ObjectManager(ListModel):
self.itemsChanged.emit()
def _update_scene_changed(self, *args):
# if args and type(args[0]) is not CuraSceneNode:
# Logger.log("d", " ascdf %s", args)
# return
self._update(*args)
## Either select or deselect an item
@pyqtSlot(int)
def changeSelection(self, index):
@ -63,6 +81,11 @@ class ObjectManager(ListModel):
self._last_selected_index = index
# testing
for node in DepthFirstIterator(Application.getInstance().getController().getScene().getRoot()):
if node.callDecoration("getLayerData"):
Logger.log("d", " ##### NODE: %s", node)
@staticmethod
def createObjectManager():
return ObjectManager()

View file

@ -6,11 +6,14 @@ from UM.Logger import Logger
class BuildPlateDecorator(SceneNodeDecorator):
def __init__(self, build_plate_number = -1):
super().__init__()
self._build_plate_number = None
self._previous_build_plate_number = None
self.setBuildPlateNumber(build_plate_number)
def setBuildPlateNumber(self, nr):
# Make sure that groups are set correctly
# setBuildPlateForSelection in CuraActions makes sure that no single childs are set.
self._previous_build_plate_number = self._build_plate_number
self._build_plate_number = nr
if self._node and self._node.callDecoration("isGroup"):
for child in self._node.getChildren():
@ -19,5 +22,9 @@ class BuildPlateDecorator(SceneNodeDecorator):
def getBuildPlateNumber(self):
return self._build_plate_number
# Used to determine from what build plate the node moved.
def getPreviousBuildPlateNumber(self):
return self._previous_build_plate_number
def __deepcopy__(self, memo):
return BuildPlateDecorator()

View file

@ -0,0 +1,40 @@
from UM.Application import Application
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode
from copy import deepcopy
## Scene nodes that are models are only seen when selecting the corresponding build plate
# Note that many other nodes can just be UM SceneNode objects.
class CuraSceneNode(SceneNode):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._outside_buildarea = True
def setOutsideBuildArea(self, new_value):
self._outside_buildarea = new_value
def isOutsideBuildArea(self):
return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0
def isVisible(self):
return super().isVisible() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().activeBuildPlate
def isSelectable(self) -> bool:
return super().isSelectable() and self.callDecoration("getBuildPlateNumber") == Application.getInstance().activeBuildPlate
## Taken from SceneNode, but replaced SceneNode with CuraSceneNode
def __deepcopy__(self, memo):
copy = CuraSceneNode()
copy.setTransformation(self.getLocalTransformation())
copy.setMeshData(self._mesh_data)
copy.setVisible(deepcopy(self._visible, memo))
copy._selectable = deepcopy(self._selectable, memo)
copy._name = deepcopy(self._name, memo)
for decorator in self._decorators:
copy.addDecorator(deepcopy(decorator, memo))
for child in self._children:
copy.addChild(deepcopy(child, memo))
self.calculateBoundingBoxMesh()
return copy

View file

@ -15,7 +15,8 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
from UM.Application import Application
from cura.Settings.ExtruderManager import ExtruderManager
from cura.QualityManager import QualityManager
from UM.Scene.SceneNode import SceneNode
#from UM.Scene.SceneNode import SceneNode
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
from cura.SliceableObjectDecorator import SliceableObjectDecorator
from cura.ZOffsetDecorator import ZOffsetDecorator

View file

@ -69,9 +69,10 @@ class CuraEngineBackend(QObject, Backend):
# Workaround to disable layer view processing if layer view is not active.
self._layer_view_active = False
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
Application.getInstance().activeBuildPlateChanged.connect(self._onActiveViewChanged)
self._onActiveViewChanged()
self._stored_layer_data = []
self._stored_optimized_layer_data = []
self._stored_optimized_layer_data = {} # key is build plate number, then arrays are stored until they go to the ProcessSlicesLayersJob
self._scene = Application.getInstance().getController().getScene()
self._scene.sceneChanged.connect(self._onSceneChanged)
@ -104,12 +105,14 @@ class CuraEngineBackend(QObject, Backend):
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
self._start_slice_job = None
self._start_slice_job_build_plate = None
self._slicing = False # Are we currently slicing?
self._restart = False # Back-end is currently restarting?
self._tool_active = False # If a tool is active, some tasks do not have to do anything
self._always_restart = True # Always restart the engine when starting a new slice. Don't keep the process running. TODO: Fix engine statelessness.
self._process_layers_job = None # The currently active job to process layers, or None if it is not processing layers.
self._need_slicing = False
# self._need_slicing = False
self._build_plates_to_be_sliced = [] # what needs slicing?
self._engine_is_fresh = True # Is the newly started engine used before or not?
self._backend_log_max_lines = 20000 # Maximum number of lines to buffer
@ -189,8 +192,9 @@ class CuraEngineBackend(QObject, Backend):
## Perform a slice of the scene.
def slice(self):
Logger.log("d", "starting to slice again!")
self._slice_start_time = time()
if not self._need_slicing:
if not self._build_plates_to_be_sliced:
self.processingProgress.emit(1.0)
self.backendStateChange.emit(BackendState.Done)
Logger.log("w", "Slice unnecessary, nothing has changed that needs reslicing.")
@ -199,7 +203,6 @@ class CuraEngineBackend(QObject, Backend):
Application.getInstance().getPrintInformation().setToZeroPrintInformation()
self._stored_layer_data = []
self._stored_optimized_layer_data = []
if self._process is None:
self._createSocket()
@ -215,6 +218,9 @@ class CuraEngineBackend(QObject, Backend):
slice_message = self._socket.createMessage("cura.proto.Slice")
self._start_slice_job = StartSliceJob.StartSliceJob(slice_message)
self._start_slice_job_build_plate = self._build_plates_to_be_sliced.pop(0)
self._stored_optimized_layer_data[self._start_slice_job_build_plate] = []
self._start_slice_job.setBuildPlate(self._start_slice_job_build_plate)
self._start_slice_job.start()
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
@ -223,7 +229,8 @@ class CuraEngineBackend(QObject, Backend):
def _terminate(self):
self._slicing = False
self._stored_layer_data = []
self._stored_optimized_layer_data = []
if self._start_slice_job_build_plate in self._stored_optimized_layer_data:
del self._stored_optimized_layer_data[self._start_slice_job_build_plate]
if self._start_slice_job is not None:
self._start_slice_job.cancel()
@ -315,10 +322,13 @@ class CuraEngineBackend(QObject, Backend):
self._error_message = Message(catalog.i18nc("@info:status", "Nothing to slice because none of the models fit the build volume. Please scale or rotate models to fit."),
title = catalog.i18nc("@info:title", "Unable to slice"))
self._error_message.show()
self.backendStateChange.emit(BackendState.Error)
#self.backendStateChange.emit(BackendState.Error)
else:
self.backendStateChange.emit(BackendState.NotStarted)
#self.backendStateChange.emit(BackendState.NotStarted)
pass
self._invokeSlice()
return
# Preparation completed, send it to the backend.
self._socket.sendMessage(job.getSliceMessage())
@ -360,27 +370,34 @@ class CuraEngineBackend(QObject, Backend):
#
# \param source The scene node that was changed.
def _onSceneChanged(self, source):
if type(source) is not SceneNode:
Logger.log("d", " ##### scene changed: %s", source)
if not issubclass(type(source), SceneNode):
return
root_scene_nodes_changed = False
build_plates_changed = set()
if source == self._scene.getRoot():
num_objects = 0
for node in DepthFirstIterator(self._scene.getRoot()):
# Only count sliceable objects
if node.callDecoration("isSliceable"):
num_objects += 1
build_plates_changed.add(node.callDecoration("getBuildPlateNumber"))
build_plates_changed.add(node.callDecoration("getPreviousBuildPlateNumber"))
if num_objects != self._last_num_objects:
self._last_num_objects = num_objects
root_scene_nodes_changed = True
else:
return
# else:
# return # ??
build_plates_changed.discard(None)
build_plates_changed.discard(-1) # object not on build plate
Logger.log("d", " #### build plates changed: %s", build_plates_changed)
if not source.callDecoration("isGroup") and not root_scene_nodes_changed:
if source.getMeshData() is None:
return
if source.getMeshData().getVertices() is None:
return
# if not source.callDecoration("isGroup") and not root_scene_nodes_changed:
# if source.getMeshData() is None:
# return
# if source.getMeshData().getVertices() is None:
# return
if self._tool_active:
# do it later, each source only has to be done once
@ -388,9 +405,24 @@ class CuraEngineBackend(QObject, Backend):
self._postponed_scene_change_sources.append(source)
return
self.needsSlicing()
if build_plates_changed:
Logger.log("d", " going to reslice")
self.stopSlicing()
self._onChanged()
for build_plate_number in build_plates_changed:
if build_plate_number not in self._build_plates_to_be_sliced:
self._build_plates_to_be_sliced.append(build_plate_number)
self.processingProgress.emit(0.0)
self.backendStateChange.emit(BackendState.NotStarted)
if not self._use_timer:
# With manually having to slice, we want to clear the old invalid layer data.
self._clearLayerData(build_plates_changed)
self._invokeSlice()
# #self.needsSlicing()
# self.stopSlicing()
# #self._onChanged()
# self._invokeSlice()
## Called when an error occurs in the socket connection towards the engine.
#
@ -410,16 +442,24 @@ class CuraEngineBackend(QObject, Backend):
Logger.log("w", "A socket error caused the connection to be reset")
## Remove old layer data (if any)
def _clearLayerData(self):
def _clearLayerData(self, build_plate_numbers = set()):
for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("getLayerData"):
if node.callDecoration("getBuildPlateNumber") in build_plate_numbers or not build_plate_numbers:
node.getParent().removeChild(node)
break
## Convenient function: set need_slicing, emit state and clear layer data
def markSliceAll(self):
if 0 not in self._build_plates_to_be_sliced:
self._build_plates_to_be_sliced.append(0)
if 1 not in self._build_plates_to_be_sliced:
self._build_plates_to_be_sliced.append(1)
if 2 not in self._build_plates_to_be_sliced:
self._build_plates_to_be_sliced.append(2)
## Convenient function: mark everything to slice, emit state and clear layer data
def needsSlicing(self):
self.stopSlicing()
self._need_slicing = True
self.markSliceAll()
self.processingProgress.emit(0.0)
self.backendStateChange.emit(BackendState.NotStarted)
if not self._use_timer:
@ -441,7 +481,7 @@ class CuraEngineBackend(QObject, Backend):
def _onStackErrorCheckFinished(self):
self._is_error_check_scheduled = False
if not self._slicing and self._need_slicing:
if not self._slicing and self._build_plates_to_be_sliced: #self._need_slicing:
self.needsSlicing()
self._onChanged()
@ -455,7 +495,7 @@ class CuraEngineBackend(QObject, Backend):
#
# \param message The protobuf message containing sliced layer data.
def _onOptimizedLayerMessage(self, message):
self._stored_optimized_layer_data.append(message)
self._stored_optimized_layer_data[self._start_slice_job_build_plate].append(message)
## Called when a progress message is received from the engine.
#
@ -464,6 +504,16 @@ class CuraEngineBackend(QObject, Backend):
self.processingProgress.emit(message.amount)
self.backendStateChange.emit(BackendState.Processing)
# testing
def _invokeSlice(self):
if self._use_timer:
# if the error check is scheduled, wait for the error check finish signal to trigger auto-slice,
# otherwise business as usual
if self._is_error_check_scheduled:
self._change_timer.stop()
else:
self._change_timer.start()
## Called when the engine sends a message that slicing is finished.
#
# \param message The protobuf message signalling that slicing is finished.
@ -481,13 +531,20 @@ class CuraEngineBackend(QObject, Backend):
self._scene.gcode_list[self._scene.gcode_list.index(line)] = replaced
self._slicing = False
self._need_slicing = False
#self._need_slicing = False
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()):
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data)
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
self._process_layers_job.start()
self._stored_optimized_layer_data = []
# See if we need to process the sliced layers job.
active_build_plate = Application.getInstance().activeBuildPlate
if self._layer_view_active and (self._process_layers_job is None or not self._process_layers_job.isRunning()) and active_build_plate == self._start_slice_job_build_plate:
self._startProcessSlicedLayersJob(active_build_plate)
self._start_slice_job_build_plate = None
Logger.log("d", "See if there is more to slice...")
# Somehow this results in an Arcus Error
# self.slice()
# Testing call slice again, allow backend to restart by using the timer
self._invokeSlice()
## Called when a g-code message is received from the engine.
#
@ -584,19 +641,26 @@ class CuraEngineBackend(QObject, Backend):
source = self._postponed_scene_change_sources.pop(0)
self._onSceneChanged(source)
def _startProcessSlicedLayersJob(self, build_plate_number):
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data[build_plate_number])
self._process_layers_job.setBuildPlate(build_plate_number)
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
self._process_layers_job.start()
del self._stored_optimized_layer_data[build_plate_number]
## Called when the user changes the active view mode.
def _onActiveViewChanged(self):
if Application.getInstance().getController().getActiveView():
view = Application.getInstance().getController().getActiveView()
application = Application.getInstance()
view = application.getController().getActiveView()
if view:
active_build_plate = application.activeBuildPlate
if view.getPluginId() == "LayerView": # If switching to layer view, we should process the layers if that hasn't been done yet.
self._layer_view_active = True
# There is data and we're not slicing at the moment
# if we are slicing, there is no need to re-calculate the data as it will be invalid in a moment.
if self._stored_optimized_layer_data and not self._slicing:
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data)
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
self._process_layers_job.start()
self._stored_optimized_layer_data = []
# TODO: what build plate I am slicing
if active_build_plate in self._stored_optimized_layer_data and not self._slicing:
self._startProcessSlicedLayersJob(active_build_plate)
else:
self._layer_view_active = False

View file

@ -17,10 +17,12 @@ from UM.Logger import Logger
from UM.Math.Vector import Vector
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
from cura.Settings.ExtruderManager import ExtruderManager
from cura import LayerDataBuilder
from cura import LayerDataDecorator
from cura import LayerPolygon
# from cura.Scene.CuraSceneNode import CuraSceneNode
import numpy
from time import time
@ -49,6 +51,7 @@ class ProcessSlicedLayersJob(Job):
self._scene = Application.getInstance().getController().getScene()
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
self._abort_requested = False
self._build_plate_number = None
## Aborts the processing of layers.
#
@ -59,7 +62,11 @@ class ProcessSlicedLayersJob(Job):
def abort(self):
self._abort_requested = True
def setBuildPlate(self, new_value):
self._build_plate_number = new_value
def run(self):
Logger.log("d", "########## Processing new layer for [%s]..." % self._build_plate_number)
start_time = time()
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
self._progress_message.show()
@ -72,16 +79,18 @@ class ProcessSlicedLayersJob(Job):
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
new_node = SceneNode()
new_node.addDecorator(BuildPlateDecorator(self._build_plate_number))
## Remove old layer data (if any)
for node in DepthFirstIterator(self._scene.getRoot()):
if node.callDecoration("getLayerData"):
node.getParent().removeChild(node)
break
if self._abort_requested:
if self._progress_message:
self._progress_message.hide()
return
# ## Remove old layer data (if any)
# for node in DepthFirstIterator(self._scene.getRoot()):
# if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number:
# Logger.log("d", " # Removing: %s", node)
# node.getParent().removeChild(node)
# #break
# if self._abort_requested:
# if self._progress_message:
# self._progress_message.hide()
# return
# Force garbage collection.
# For some reason, Python has a tendency to keep the layer data

View file

@ -10,12 +10,13 @@ from UM.Job import Job
from UM.Application import Application
from UM.Logger import Logger
from UM.Scene.SceneNode import SceneNode
#from UM.Scene.SceneNode import SceneNode
from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
from UM.Settings.Validator import ValidatorState
from UM.Settings.SettingRelation import RelationType
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
from cura.OneAtATimeIterator import OneAtATimeIterator
from cura.Settings.ExtruderManager import ExtruderManager
@ -58,10 +59,14 @@ class StartSliceJob(Job):
self._scene = Application.getInstance().getController().getScene()
self._slice_message = slice_message
self._is_cancelled = False
self._build_plate_number = None
def getSliceMessage(self):
return self._slice_message
def setBuildPlate(self, build_plate_number):
self._build_plate_number = build_plate_number
## Check if a stack has any errors.
## returns true if it has errors, false otherwise.
def _checkStackForErrors(self, stack):
@ -78,6 +83,10 @@ class StartSliceJob(Job):
## Runs the job that initiates the slicing.
def run(self):
if self._build_plate_number is None:
self.setResult(StartJobResult.Error)
return
stack = Application.getInstance().getGlobalContainerStack()
if not stack:
self.setResult(StartJobResult.Error)
@ -141,14 +150,12 @@ class StartSliceJob(Job):
for node in DepthFirstIterator(self._scene.getRoot()):
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
# temp hack to filter on build plate 0
if (node.callDecoration("getBuildPlateNumber") == 0):
if not getattr(node, "_outside_buildarea", False)\
or (node.callDecoration("getStack") and any(node.callDecoration("getStack").getProperty(setting, "value") for setting in self._not_printed_mesh_settings)):
if (node.callDecoration("getBuildPlateNumber") == self._build_plate_number):
if not getattr(node, "_outside_buildarea", False) or (node.callDecoration("getStack") and any(node.callDecoration("getStack").getProperty(setting, "value") for setting in self._not_printed_mesh_settings)):
temp_list.append(node)
Job.yieldThread()
Logger.log("d", " objects to be sliced: %s", temp_list)
if temp_list:
object_groups.append(temp_list)

View file

@ -8,12 +8,14 @@ from PyQt5.QtCore import Qt
from UM.Mesh.MeshReader import MeshReader
from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Scene.SceneNode import SceneNode
#from UM.Scene.SceneNode import SceneNode
from UM.Math.Vector import Vector
from UM.Job import Job
from UM.Logger import Logger
from .ImageReaderUI import ImageReaderUI
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
class ImageReader(MeshReader):
def __init__(self):

View file

@ -66,13 +66,14 @@ class LayerPass(RenderPass):
self.bind()
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay)
active_build_plate = Application.getInstance().activeBuildPlate
for node in DepthFirstIterator(self._scene.getRoot()):
if isinstance(node, ToolHandle):
tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
elif isinstance(node, SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible():
elif issubclass(type(node), SceneNode) and (node.getMeshData() or node.callDecoration("isBlockSlicing")) and node.isVisible() and node.callDecoration("getBuildPlateNumber") == active_build_plate:
layer_data = node.callDecoration("getLayerData")
if not layer_data:
continue

View file

@ -75,7 +75,7 @@ class SolidView(View):
for node in DepthFirstIterator(scene.getRoot()):
if not node.render(renderer):
if node.getMeshData() and node.isVisible() and (node.callDecoration("getBuildPlateNumber") == activeBuildPlateNumber):
if node.getMeshData() and node.isVisible(): # and (node.callDecoration("getBuildPlateNumber") == activeBuildPlateNumber):
uniforms = {}
shade_factor = 1.0

View file

@ -11,7 +11,8 @@ from UM.Math.Matrix import Matrix
from UM.Math.Vector import Vector
from UM.Mesh.MeshBuilder import MeshBuilder
from UM.Mesh.MeshReader import MeshReader
from UM.Scene.SceneNode import SceneNode
#from UM.Scene.SceneNode import SceneNode
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
MYPY = False
try:

View file

@ -409,6 +409,7 @@ UM.MainWindow
{
id: objectsList;
visible: false;
//z: -10;
anchors
{
top: objectsButton.top;

View file

@ -55,7 +55,7 @@ Rectangle
//anchors.right: parent.right
width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30
text: Cura.ObjectManager.getItem(index).name;
color: Cura.ObjectManager.getItem(index).isSelected ? palette.highlightedText : palette.text
color: Cura.ObjectManager.getItem(index).isSelected ? palette.highlightedText : (Cura.ObjectManager.getItem(index).isOutsideBuildArea ? palette.mid : palette.text)
elide: Text.ElideRight
}
@ -95,7 +95,7 @@ Rectangle
topMargin: UM.Theme.getSize("default_margin").height;
left: parent.left;
leftMargin: UM.Theme.getSize("default_margin").height;
bottom: buildPlateSelection.top;
bottom: filterBuildPlateCheckbox.top;
bottomMargin: UM.Theme.getSize("default_margin").height;
}
@ -115,6 +115,25 @@ Rectangle
}
}
CheckBox
{
id: filterBuildPlateCheckbox
checked: boolCheck(UM.Preferences.getValue("view/filter_current_build_plate"))
onClicked: UM.Preferences.setValue("view/filter_current_build_plate", checked)
text: catalog.i18nc("@option:check","Filter active build plate");
anchors
{
left: parent.left;
topMargin: UM.Theme.getSize("default_margin").height;
bottomMargin: UM.Theme.getSize("default_margin").height;
leftMargin: UM.Theme.getSize("default_margin").height;
bottom: buildPlateSelection.top;
}
}
ListModel
{
id: buildPlatesModel

View file

@ -451,6 +451,20 @@ UM.PreferencesPage
text: catalog.i18nc("@label","Opening and saving files")
}
UM.TooltipArea {
width: childrenRect.width
height: childrenRect.height
text: catalog.i18nc("@info:tooltip","Should newly loaded models be arranged on the build palte?")
CheckBox
{
id: arrangeOnLoadCheckbox
text: catalog.i18nc("@option:check","Arrange objects on load")
checked: boolCheck(UM.Preferences.getValue("cura/arrange_objects_on_load"))
onCheckedChanged: UM.Preferences.setValue("cura/arrange_objects_on_load", checked)
}
}
UM.TooltipArea {
width: childrenRect.width
height: childrenRect.height