mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
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:
parent
41d5ec86a3
commit
e21acd1a07
18 changed files with 468 additions and 260 deletions
|
@ -40,7 +40,7 @@ class Arrange:
|
||||||
# \param fixed_nodes Scene nodes to be placed
|
# \param fixed_nodes Scene nodes to be placed
|
||||||
@classmethod
|
@classmethod
|
||||||
def create(cls, scene_root = None, fixed_nodes = None, scale = 0.5, x = 220, y = 220):
|
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()
|
arranger.centerFirst()
|
||||||
|
|
||||||
if fixed_nodes is None:
|
if fixed_nodes is None:
|
||||||
|
|
|
@ -112,7 +112,6 @@ class ArrangeObjectsAllBuildPlatesJob(Job):
|
||||||
# start_priority = 0
|
# start_priority = 0
|
||||||
|
|
||||||
while try_placement:
|
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
|
# 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():
|
while current_build_plate_number >= arrange_array.count():
|
||||||
arrange_array.add()
|
arrange_array.add()
|
||||||
|
|
|
@ -6,7 +6,6 @@ from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Resources import Resources
|
from UM.Resources import Resources
|
||||||
from UM.Math.Color import Color
|
from UM.Math.Color import Color
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with.
|
from UM.Mesh.MeshBuilder import MeshBuilder # To create a mesh to display the convex hull with.
|
||||||
|
|
||||||
from UM.View.GL.OpenGL import OpenGL
|
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_diffuseColor", self._color)
|
||||||
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
|
ConvexHullNode.shader.setUniformValue("u_opacity", 0.6)
|
||||||
|
|
||||||
if self.getParent():
|
if self.getParent() and self.getParent().callDecoration("getBuildPlateNumber") == Application.getInstance().activeBuildPlate:
|
||||||
if self.getMeshData():
|
if self.getMeshData():
|
||||||
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
|
renderer.queueNode(self, transparent = True, shader = ConvexHullNode.shader, backface_cull = True, sort = -8)
|
||||||
if self._convex_hull_head_mesh:
|
if self._convex_hull_head_mesh:
|
||||||
|
|
|
@ -33,6 +33,7 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation
|
||||||
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
|
||||||
from UM.Operations.GroupedOperation import GroupedOperation
|
from UM.Operations.GroupedOperation import GroupedOperation
|
||||||
from UM.Operations.SetTransformOperation import SetTransformOperation
|
from UM.Operations.SetTransformOperation import SetTransformOperation
|
||||||
|
|
||||||
from cura.Arrange import Arrange
|
from cura.Arrange import Arrange
|
||||||
from cura.ShapeArray import ShapeArray
|
from cura.ShapeArray import ShapeArray
|
||||||
from cura.ConvexHullDecorator import ConvexHullDecorator
|
from cura.ConvexHullDecorator import ConvexHullDecorator
|
||||||
|
@ -41,6 +42,7 @@ from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
||||||
from cura.BlockSlicingDecorator import BlockSlicingDecorator
|
from cura.BlockSlicingDecorator import BlockSlicingDecorator
|
||||||
# research
|
# research
|
||||||
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
|
||||||
from cura.ArrangeObjectsJob import ArrangeObjectsJob
|
from cura.ArrangeObjectsJob import ArrangeObjectsJob
|
||||||
from cura.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
|
from cura.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
|
||||||
|
@ -307,11 +309,13 @@ class CuraApplication(QtApplication):
|
||||||
preferences.addPreference("cura/asked_dialog_on_project_save", False)
|
preferences.addPreference("cura/asked_dialog_on_project_save", False)
|
||||||
preferences.addPreference("cura/choice_on_profile_override", "always_ask")
|
preferences.addPreference("cura/choice_on_profile_override", "always_ask")
|
||||||
preferences.addPreference("cura/choice_on_open_project", "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/currency", "€")
|
||||||
preferences.addPreference("cura/material_settings", "{}")
|
preferences.addPreference("cura/material_settings", "{}")
|
||||||
|
|
||||||
preferences.addPreference("view/invert_zoom", False)
|
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")
|
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
|
scene_bounding_box = None
|
||||||
is_block_slicing_node = False
|
is_block_slicing_node = False
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
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
|
continue
|
||||||
if node.callDecoration("isBlockSlicing"):
|
if node.callDecoration("isBlockSlicing"):
|
||||||
is_block_slicing_node = True
|
is_block_slicing_node = True
|
||||||
|
@ -1013,7 +1017,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
Selection.clear()
|
Selection.clear()
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
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)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
if not node.isSelectable():
|
if not node.isSelectable():
|
||||||
continue # i.e. node with layer data
|
continue # i.e. node with layer data
|
||||||
|
if not node.callDecoration("isSliceable"):
|
||||||
|
continue # i.e. node with layer data
|
||||||
|
|
||||||
Selection.add(node)
|
Selection.add(node)
|
||||||
|
|
||||||
## Delete all nodes containing mesh data in the scene.
|
## Delete all nodes containing mesh data in the scene.
|
||||||
|
@ -1032,7 +1039,7 @@ class CuraApplication(QtApplication):
|
||||||
|
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if (not node.getMeshData() and not node.callDecoration("getLayerData")) and not node.callDecoration("isGroup"):
|
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.
|
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")
|
Logger.log("i", "Resetting all scene translations")
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
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")
|
Logger.log("i", "Resetting all scene transformations")
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
continue # Node that doesnt have a mesh and is not a group.
|
||||||
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
if node.getParent() and node.getParent().callDecoration("isGroup"):
|
||||||
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
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
|
continue # i.e. node with layer data
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
|
||||||
|
@ -1109,7 +1116,27 @@ class CuraApplication(QtApplication):
|
||||||
def arrangeObjectsToAllBuildPlates(self):
|
def arrangeObjectsToAllBuildPlates(self):
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
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
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
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)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
if not node.isSelectable():
|
if not node.isSelectable():
|
||||||
continue # i.e. node with layer data
|
continue # i.e. node with layer data
|
||||||
# Skip nodes that are too big
|
if not node.callDecoration("isSliceable"):
|
||||||
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():
|
|
||||||
continue # i.e. node with layer data
|
continue # i.e. node with layer data
|
||||||
if node.callDecoration("getBuildPlateNumber") == self._active_build_plate:
|
if node.callDecoration("getBuildPlateNumber") == self._active_build_plate:
|
||||||
# Skip nodes that are too big
|
# 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
|
# What nodes are on the build plate and are not being moved
|
||||||
fixed_nodes = []
|
fixed_nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
||||||
if type(node) is not SceneNode:
|
if not issubclass(type(node), SceneNode):
|
||||||
continue
|
continue
|
||||||
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
if not node.getMeshData() and not node.callDecoration("isGroup"):
|
||||||
continue # Node that doesnt have a mesh and is not a group.
|
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)
|
continue # Grouped nodes don't need resetting as their parent (the group) is resetted)
|
||||||
if not node.isSelectable():
|
if not node.isSelectable():
|
||||||
continue # i.e. node with layer data
|
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
|
if node in nodes: # exclude selected node from fixed_nodes
|
||||||
continue
|
continue
|
||||||
fixed_nodes.append(node)
|
fixed_nodes.append(node)
|
||||||
|
@ -1176,7 +1188,7 @@ class CuraApplication(QtApplication):
|
||||||
Logger.log("i", "Reloading all loaded mesh data.")
|
Logger.log("i", "Reloading all loaded mesh data.")
|
||||||
nodes = []
|
nodes = []
|
||||||
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
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
|
continue
|
||||||
|
|
||||||
nodes.append(node)
|
nodes.append(node)
|
||||||
|
@ -1267,7 +1279,7 @@ class CuraApplication(QtApplication):
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def groupSelected(self):
|
def groupSelected(self):
|
||||||
# Create a group-node
|
# Create a group-node
|
||||||
group_node = SceneNode()
|
group_node = CuraSceneNode()
|
||||||
group_decorator = GroupDecorator()
|
group_decorator = GroupDecorator()
|
||||||
group_node.addDecorator(group_decorator)
|
group_node.addDecorator(group_decorator)
|
||||||
group_node.addDecorator(ConvexHullDecorator())
|
group_node.addDecorator(ConvexHullDecorator())
|
||||||
|
@ -1413,11 +1425,15 @@ class CuraApplication(QtApplication):
|
||||||
min_offset = 8
|
min_offset = 8
|
||||||
|
|
||||||
self.fileLoaded.emit(filename)
|
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.setSelectable(True)
|
||||||
node.setName(os.path.basename(filename))
|
node.setName(os.path.basename(filename))
|
||||||
node.addDecorator(BuildPlateDecorator())
|
|
||||||
|
|
||||||
extension = os.path.splitext(filename)[1]
|
extension = os.path.splitext(filename)[1]
|
||||||
if extension.lower() in self._non_sliceable_extensions:
|
if extension.lower() in self._non_sliceable_extensions:
|
||||||
|
@ -1442,6 +1458,7 @@ class CuraApplication(QtApplication):
|
||||||
if not child.getDecorator(ConvexHullDecorator):
|
if not child.getDecorator(ConvexHullDecorator):
|
||||||
child.addDecorator(ConvexHullDecorator())
|
child.addDecorator(ConvexHullDecorator())
|
||||||
|
|
||||||
|
if arrange_objects_on_load:
|
||||||
if node.callDecoration("isSliceable"):
|
if node.callDecoration("isSliceable"):
|
||||||
# Only check position if it's not already blatantly obvious that it won't fit.
|
# 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:
|
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
|
# 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, _ = arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step = 10)
|
||||||
|
|
||||||
|
node.addDecorator(BuildPlateDecorator(target_build_plate))
|
||||||
|
|
||||||
op = AddSceneNodeOperation(node, scene.getRoot())
|
op = AddSceneNodeOperation(node, scene.getRoot())
|
||||||
op.push()
|
op.push()
|
||||||
scene.sceneChanged.emit(node)
|
scene.sceneChanged.emit(node)
|
||||||
|
@ -1494,6 +1513,8 @@ class CuraApplication(QtApplication):
|
||||||
#### research - hacky place for these kind of thing
|
#### research - hacky place for these kind of thing
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def setActiveBuildPlate(self, nr):
|
def setActiveBuildPlate(self, nr):
|
||||||
|
if nr == self._active_build_plate:
|
||||||
|
return
|
||||||
Logger.log("d", "Select build plate: %s" % nr)
|
Logger.log("d", "Select build plate: %s" % nr)
|
||||||
self._active_build_plate = nr
|
self._active_build_plate = nr
|
||||||
|
|
||||||
|
|
|
@ -7,23 +7,35 @@ from UM.Scene.SceneNode import SceneNode
|
||||||
from UM.Scene.Selection import Selection
|
from UM.Scene.Selection import Selection
|
||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
from PyQt5.QtWidgets import QApplication
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
#from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
from UM.Preferences import Preferences
|
||||||
|
|
||||||
|
|
||||||
class ObjectManager(ListModel):
|
class ObjectManager(ListModel):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._last_selected_index = 0
|
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):
|
def _update(self, *args):
|
||||||
nodes = []
|
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()):
|
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
|
continue
|
||||||
nodes.append({
|
nodes.append({
|
||||||
"name": node.getName(),
|
"name": node.getName(),
|
||||||
"isSelected": Selection.isSelected(node),
|
"isSelected": Selection.isSelected(node),
|
||||||
"buildPlateNumber": node.callDecoration("getBuildPlateNumber"),
|
"isOutsideBuildArea": node.isOutsideBuildArea(),
|
||||||
|
"buildPlateNumber": node_build_plate_number,
|
||||||
"node": node
|
"node": node
|
||||||
})
|
})
|
||||||
nodes = sorted(nodes, key=lambda n: n["name"])
|
nodes = sorted(nodes, key=lambda n: n["name"])
|
||||||
|
@ -31,6 +43,12 @@ class ObjectManager(ListModel):
|
||||||
|
|
||||||
self.itemsChanged.emit()
|
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
|
## Either select or deselect an item
|
||||||
@pyqtSlot(int)
|
@pyqtSlot(int)
|
||||||
def changeSelection(self, index):
|
def changeSelection(self, index):
|
||||||
|
@ -63,6 +81,11 @@ class ObjectManager(ListModel):
|
||||||
|
|
||||||
self._last_selected_index = index
|
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
|
@staticmethod
|
||||||
def createObjectManager():
|
def createObjectManager():
|
||||||
return ObjectManager()
|
return ObjectManager()
|
||||||
|
|
|
@ -6,11 +6,14 @@ from UM.Logger import Logger
|
||||||
class BuildPlateDecorator(SceneNodeDecorator):
|
class BuildPlateDecorator(SceneNodeDecorator):
|
||||||
def __init__(self, build_plate_number = -1):
|
def __init__(self, build_plate_number = -1):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self._build_plate_number = None
|
||||||
|
self._previous_build_plate_number = None
|
||||||
self.setBuildPlateNumber(build_plate_number)
|
self.setBuildPlateNumber(build_plate_number)
|
||||||
|
|
||||||
def setBuildPlateNumber(self, nr):
|
def setBuildPlateNumber(self, nr):
|
||||||
# Make sure that groups are set correctly
|
# Make sure that groups are set correctly
|
||||||
# setBuildPlateForSelection in CuraActions makes sure that no single childs are set.
|
# 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
|
self._build_plate_number = nr
|
||||||
if self._node and self._node.callDecoration("isGroup"):
|
if self._node and self._node.callDecoration("isGroup"):
|
||||||
for child in self._node.getChildren():
|
for child in self._node.getChildren():
|
||||||
|
@ -19,5 +22,9 @@ class BuildPlateDecorator(SceneNodeDecorator):
|
||||||
def getBuildPlateNumber(self):
|
def getBuildPlateNumber(self):
|
||||||
return self._build_plate_number
|
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):
|
def __deepcopy__(self, memo):
|
||||||
return BuildPlateDecorator()
|
return BuildPlateDecorator()
|
||||||
|
|
40
cura/Scene/CuraSceneNode.py
Normal file
40
cura/Scene/CuraSceneNode.py
Normal 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
|
|
@ -15,7 +15,8 @@ from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura.QualityManager import QualityManager
|
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.SliceableObjectDecorator import SliceableObjectDecorator
|
||||||
from cura.ZOffsetDecorator import ZOffsetDecorator
|
from cura.ZOffsetDecorator import ZOffsetDecorator
|
||||||
|
|
||||||
|
|
|
@ -69,9 +69,10 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
# Workaround to disable layer view processing if layer view is not active.
|
# Workaround to disable layer view processing if layer view is not active.
|
||||||
self._layer_view_active = False
|
self._layer_view_active = False
|
||||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||||
|
Application.getInstance().activeBuildPlateChanged.connect(self._onActiveViewChanged)
|
||||||
self._onActiveViewChanged()
|
self._onActiveViewChanged()
|
||||||
self._stored_layer_data = []
|
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 = Application.getInstance().getController().getScene()
|
||||||
self._scene.sceneChanged.connect(self._onSceneChanged)
|
self._scene.sceneChanged.connect(self._onSceneChanged)
|
||||||
|
@ -104,12 +105,14 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
self._message_handlers["cura.proto.SlicingFinished"] = self._onSlicingFinishedMessage
|
||||||
|
|
||||||
self._start_slice_job = None
|
self._start_slice_job = None
|
||||||
|
self._start_slice_job_build_plate = None
|
||||||
self._slicing = False # Are we currently slicing?
|
self._slicing = False # Are we currently slicing?
|
||||||
self._restart = False # Back-end is currently restarting?
|
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._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._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._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._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
|
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.
|
## Perform a slice of the scene.
|
||||||
def slice(self):
|
def slice(self):
|
||||||
|
Logger.log("d", "starting to slice again!")
|
||||||
self._slice_start_time = time()
|
self._slice_start_time = time()
|
||||||
if not self._need_slicing:
|
if not self._build_plates_to_be_sliced:
|
||||||
self.processingProgress.emit(1.0)
|
self.processingProgress.emit(1.0)
|
||||||
self.backendStateChange.emit(BackendState.Done)
|
self.backendStateChange.emit(BackendState.Done)
|
||||||
Logger.log("w", "Slice unnecessary, nothing has changed that needs reslicing.")
|
Logger.log("w", "Slice unnecessary, nothing has changed that needs reslicing.")
|
||||||
|
@ -199,7 +203,6 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
Application.getInstance().getPrintInformation().setToZeroPrintInformation()
|
Application.getInstance().getPrintInformation().setToZeroPrintInformation()
|
||||||
|
|
||||||
self._stored_layer_data = []
|
self._stored_layer_data = []
|
||||||
self._stored_optimized_layer_data = []
|
|
||||||
|
|
||||||
if self._process is None:
|
if self._process is None:
|
||||||
self._createSocket()
|
self._createSocket()
|
||||||
|
@ -215,6 +218,9 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
slice_message = self._socket.createMessage("cura.proto.Slice")
|
slice_message = self._socket.createMessage("cura.proto.Slice")
|
||||||
self._start_slice_job = StartSliceJob.StartSliceJob(slice_message)
|
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.start()
|
||||||
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
self._start_slice_job.finished.connect(self._onStartSliceCompleted)
|
||||||
|
|
||||||
|
@ -223,7 +229,8 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
def _terminate(self):
|
def _terminate(self):
|
||||||
self._slicing = False
|
self._slicing = False
|
||||||
self._stored_layer_data = []
|
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:
|
if self._start_slice_job is not None:
|
||||||
self._start_slice_job.cancel()
|
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."),
|
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"))
|
title = catalog.i18nc("@info:title", "Unable to slice"))
|
||||||
self._error_message.show()
|
self._error_message.show()
|
||||||
self.backendStateChange.emit(BackendState.Error)
|
#self.backendStateChange.emit(BackendState.Error)
|
||||||
else:
|
else:
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
#self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
|
pass
|
||||||
|
self._invokeSlice()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Preparation completed, send it to the backend.
|
# Preparation completed, send it to the backend.
|
||||||
self._socket.sendMessage(job.getSliceMessage())
|
self._socket.sendMessage(job.getSliceMessage())
|
||||||
|
|
||||||
|
@ -360,27 +370,34 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
#
|
#
|
||||||
# \param source The scene node that was changed.
|
# \param source The scene node that was changed.
|
||||||
def _onSceneChanged(self, source):
|
def _onSceneChanged(self, source):
|
||||||
if type(source) is not SceneNode:
|
Logger.log("d", " ##### scene changed: %s", source)
|
||||||
|
if not issubclass(type(source), SceneNode):
|
||||||
return
|
return
|
||||||
|
|
||||||
root_scene_nodes_changed = False
|
root_scene_nodes_changed = False
|
||||||
|
build_plates_changed = set()
|
||||||
if source == self._scene.getRoot():
|
if source == self._scene.getRoot():
|
||||||
num_objects = 0
|
num_objects = 0
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
# Only count sliceable objects
|
# Only count sliceable objects
|
||||||
if node.callDecoration("isSliceable"):
|
if node.callDecoration("isSliceable"):
|
||||||
num_objects += 1
|
num_objects += 1
|
||||||
|
build_plates_changed.add(node.callDecoration("getBuildPlateNumber"))
|
||||||
|
build_plates_changed.add(node.callDecoration("getPreviousBuildPlateNumber"))
|
||||||
if num_objects != self._last_num_objects:
|
if num_objects != self._last_num_objects:
|
||||||
self._last_num_objects = num_objects
|
self._last_num_objects = num_objects
|
||||||
root_scene_nodes_changed = True
|
root_scene_nodes_changed = True
|
||||||
else:
|
# else:
|
||||||
return
|
# 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 not source.callDecoration("isGroup") and not root_scene_nodes_changed:
|
||||||
if source.getMeshData() is None:
|
# if source.getMeshData() is None:
|
||||||
return
|
# return
|
||||||
if source.getMeshData().getVertices() is None:
|
# if source.getMeshData().getVertices() is None:
|
||||||
return
|
# return
|
||||||
|
|
||||||
if self._tool_active:
|
if self._tool_active:
|
||||||
# do it later, each source only has to be done once
|
# 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)
|
self._postponed_scene_change_sources.append(source)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.needsSlicing()
|
if build_plates_changed:
|
||||||
|
Logger.log("d", " going to reslice")
|
||||||
self.stopSlicing()
|
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.
|
## 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")
|
Logger.log("w", "A socket error caused the connection to be reset")
|
||||||
|
|
||||||
## Remove old layer data (if any)
|
## Remove old layer data (if any)
|
||||||
def _clearLayerData(self):
|
def _clearLayerData(self, build_plate_numbers = set()):
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if node.callDecoration("getLayerData"):
|
if node.callDecoration("getLayerData"):
|
||||||
|
if node.callDecoration("getBuildPlateNumber") in build_plate_numbers or not build_plate_numbers:
|
||||||
node.getParent().removeChild(node)
|
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):
|
def needsSlicing(self):
|
||||||
self.stopSlicing()
|
self.stopSlicing()
|
||||||
self._need_slicing = True
|
self.markSliceAll()
|
||||||
self.processingProgress.emit(0.0)
|
self.processingProgress.emit(0.0)
|
||||||
self.backendStateChange.emit(BackendState.NotStarted)
|
self.backendStateChange.emit(BackendState.NotStarted)
|
||||||
if not self._use_timer:
|
if not self._use_timer:
|
||||||
|
@ -441,7 +481,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
|
|
||||||
def _onStackErrorCheckFinished(self):
|
def _onStackErrorCheckFinished(self):
|
||||||
self._is_error_check_scheduled = False
|
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.needsSlicing()
|
||||||
self._onChanged()
|
self._onChanged()
|
||||||
|
|
||||||
|
@ -455,7 +495,7 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
#
|
#
|
||||||
# \param message The protobuf message containing sliced layer data.
|
# \param message The protobuf message containing sliced layer data.
|
||||||
def _onOptimizedLayerMessage(self, message):
|
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.
|
## Called when a progress message is received from the engine.
|
||||||
#
|
#
|
||||||
|
@ -464,6 +504,16 @@ class CuraEngineBackend(QObject, Backend):
|
||||||
self.processingProgress.emit(message.amount)
|
self.processingProgress.emit(message.amount)
|
||||||
self.backendStateChange.emit(BackendState.Processing)
|
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.
|
## Called when the engine sends a message that slicing is finished.
|
||||||
#
|
#
|
||||||
# \param message The protobuf message signalling 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._scene.gcode_list[self._scene.gcode_list.index(line)] = replaced
|
||||||
|
|
||||||
self._slicing = False
|
self._slicing = False
|
||||||
self._need_slicing = False
|
#self._need_slicing = False
|
||||||
Logger.log("d", "Slicing took %s seconds", time() - self._slice_start_time )
|
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)
|
# See if we need to process the sliced layers job.
|
||||||
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
active_build_plate = Application.getInstance().activeBuildPlate
|
||||||
self._process_layers_job.start()
|
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._stored_optimized_layer_data = []
|
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.
|
## 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)
|
source = self._postponed_scene_change_sources.pop(0)
|
||||||
self._onSceneChanged(source)
|
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.
|
## Called when the user changes the active view mode.
|
||||||
def _onActiveViewChanged(self):
|
def _onActiveViewChanged(self):
|
||||||
if Application.getInstance().getController().getActiveView():
|
application = Application.getInstance()
|
||||||
view = Application.getInstance().getController().getActiveView()
|
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.
|
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
|
self._layer_view_active = True
|
||||||
# There is data and we're not slicing at the moment
|
# 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 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:
|
# TODO: what build plate I am slicing
|
||||||
self._process_layers_job = ProcessSlicedLayersJob.ProcessSlicedLayersJob(self._stored_optimized_layer_data)
|
if active_build_plate in self._stored_optimized_layer_data and not self._slicing:
|
||||||
self._process_layers_job.finished.connect(self._onProcessLayersFinished)
|
self._startProcessSlicedLayersJob(active_build_plate)
|
||||||
self._process_layers_job.start()
|
|
||||||
self._stored_optimized_layer_data = []
|
|
||||||
else:
|
else:
|
||||||
self._layer_view_active = False
|
self._layer_view_active = False
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,12 @@ from UM.Logger import Logger
|
||||||
|
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
|
|
||||||
|
from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
from cura import LayerDataBuilder
|
from cura import LayerDataBuilder
|
||||||
from cura import LayerDataDecorator
|
from cura import LayerDataDecorator
|
||||||
from cura import LayerPolygon
|
from cura import LayerPolygon
|
||||||
|
# from cura.Scene.CuraSceneNode import CuraSceneNode
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from time import time
|
from time import time
|
||||||
|
@ -49,6 +51,7 @@ class ProcessSlicedLayersJob(Job):
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
|
self._progress_message = Message(catalog.i18nc("@info:status", "Processing Layers"), 0, False, -1)
|
||||||
self._abort_requested = False
|
self._abort_requested = False
|
||||||
|
self._build_plate_number = None
|
||||||
|
|
||||||
## Aborts the processing of layers.
|
## Aborts the processing of layers.
|
||||||
#
|
#
|
||||||
|
@ -59,7 +62,11 @@ class ProcessSlicedLayersJob(Job):
|
||||||
def abort(self):
|
def abort(self):
|
||||||
self._abort_requested = True
|
self._abort_requested = True
|
||||||
|
|
||||||
|
def setBuildPlate(self, new_value):
|
||||||
|
self._build_plate_number = new_value
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
Logger.log("d", "########## Processing new layer for [%s]..." % self._build_plate_number)
|
||||||
start_time = time()
|
start_time = time()
|
||||||
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
if Application.getInstance().getController().getActiveView().getPluginId() == "LayerView":
|
||||||
self._progress_message.show()
|
self._progress_message.show()
|
||||||
|
@ -72,16 +79,18 @@ class ProcessSlicedLayersJob(Job):
|
||||||
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
Application.getInstance().getController().activeViewChanged.connect(self._onActiveViewChanged)
|
||||||
|
|
||||||
new_node = SceneNode()
|
new_node = SceneNode()
|
||||||
|
new_node.addDecorator(BuildPlateDecorator(self._build_plate_number))
|
||||||
|
|
||||||
## Remove old layer data (if any)
|
# ## Remove old layer data (if any)
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
# for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if node.callDecoration("getLayerData"):
|
# if node.callDecoration("getLayerData") and node.callDecoration("getBuildPlateNumber") == self._build_plate_number:
|
||||||
node.getParent().removeChild(node)
|
# Logger.log("d", " # Removing: %s", node)
|
||||||
break
|
# node.getParent().removeChild(node)
|
||||||
if self._abort_requested:
|
# #break
|
||||||
if self._progress_message:
|
# if self._abort_requested:
|
||||||
self._progress_message.hide()
|
# if self._progress_message:
|
||||||
return
|
# self._progress_message.hide()
|
||||||
|
# return
|
||||||
|
|
||||||
# Force garbage collection.
|
# Force garbage collection.
|
||||||
# For some reason, Python has a tendency to keep the layer data
|
# For some reason, Python has a tendency to keep the layer data
|
||||||
|
|
|
@ -10,12 +10,13 @@ from UM.Job import Job
|
||||||
from UM.Application import Application
|
from UM.Application import Application
|
||||||
from UM.Logger import Logger
|
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.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
||||||
|
|
||||||
from UM.Settings.Validator import ValidatorState
|
from UM.Settings.Validator import ValidatorState
|
||||||
from UM.Settings.SettingRelation import RelationType
|
from UM.Settings.SettingRelation import RelationType
|
||||||
|
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||||
from cura.OneAtATimeIterator import OneAtATimeIterator
|
from cura.OneAtATimeIterator import OneAtATimeIterator
|
||||||
from cura.Settings.ExtruderManager import ExtruderManager
|
from cura.Settings.ExtruderManager import ExtruderManager
|
||||||
|
|
||||||
|
@ -58,10 +59,14 @@ class StartSliceJob(Job):
|
||||||
self._scene = Application.getInstance().getController().getScene()
|
self._scene = Application.getInstance().getController().getScene()
|
||||||
self._slice_message = slice_message
|
self._slice_message = slice_message
|
||||||
self._is_cancelled = False
|
self._is_cancelled = False
|
||||||
|
self._build_plate_number = None
|
||||||
|
|
||||||
def getSliceMessage(self):
|
def getSliceMessage(self):
|
||||||
return self._slice_message
|
return self._slice_message
|
||||||
|
|
||||||
|
def setBuildPlate(self, build_plate_number):
|
||||||
|
self._build_plate_number = build_plate_number
|
||||||
|
|
||||||
## Check if a stack has any errors.
|
## Check if a stack has any errors.
|
||||||
## returns true if it has errors, false otherwise.
|
## returns true if it has errors, false otherwise.
|
||||||
def _checkStackForErrors(self, stack):
|
def _checkStackForErrors(self, stack):
|
||||||
|
@ -78,6 +83,10 @@ class StartSliceJob(Job):
|
||||||
|
|
||||||
## Runs the job that initiates the slicing.
|
## Runs the job that initiates the slicing.
|
||||||
def run(self):
|
def run(self):
|
||||||
|
if self._build_plate_number is None:
|
||||||
|
self.setResult(StartJobResult.Error)
|
||||||
|
return
|
||||||
|
|
||||||
stack = Application.getInstance().getGlobalContainerStack()
|
stack = Application.getInstance().getGlobalContainerStack()
|
||||||
if not stack:
|
if not stack:
|
||||||
self.setResult(StartJobResult.Error)
|
self.setResult(StartJobResult.Error)
|
||||||
|
@ -141,14 +150,12 @@ class StartSliceJob(Job):
|
||||||
for node in DepthFirstIterator(self._scene.getRoot()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
if type(node) is SceneNode and node.getMeshData() and node.getMeshData().getVertices() is not None:
|
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") == self._build_plate_number):
|
||||||
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 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)
|
temp_list.append(node)
|
||||||
Job.yieldThread()
|
Job.yieldThread()
|
||||||
|
|
||||||
|
Logger.log("d", " objects to be sliced: %s", temp_list)
|
||||||
if temp_list:
|
if temp_list:
|
||||||
object_groups.append(temp_list)
|
object_groups.append(temp_list)
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,14 @@ from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
from UM.Mesh.MeshReader import MeshReader
|
from UM.Mesh.MeshReader import MeshReader
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
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.Math.Vector import Vector
|
||||||
from UM.Job import Job
|
from UM.Job import Job
|
||||||
from UM.Logger import Logger
|
from UM.Logger import Logger
|
||||||
from .ImageReaderUI import ImageReaderUI
|
from .ImageReaderUI import ImageReaderUI
|
||||||
|
|
||||||
|
from cura.Scene.CuraSceneNode import CuraSceneNode as SceneNode
|
||||||
|
|
||||||
|
|
||||||
class ImageReader(MeshReader):
|
class ImageReader(MeshReader):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -66,13 +66,14 @@ class LayerPass(RenderPass):
|
||||||
self.bind()
|
self.bind()
|
||||||
|
|
||||||
tool_handle_batch = RenderBatch(self._tool_handle_shader, type = RenderBatch.RenderType.Overlay)
|
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()):
|
for node in DepthFirstIterator(self._scene.getRoot()):
|
||||||
|
|
||||||
if isinstance(node, ToolHandle):
|
if isinstance(node, ToolHandle):
|
||||||
tool_handle_batch.addItem(node.getWorldTransformation(), mesh = node.getSolidMesh())
|
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")
|
layer_data = node.callDecoration("getLayerData")
|
||||||
if not layer_data:
|
if not layer_data:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -75,7 +75,7 @@ class SolidView(View):
|
||||||
|
|
||||||
for node in DepthFirstIterator(scene.getRoot()):
|
for node in DepthFirstIterator(scene.getRoot()):
|
||||||
if not node.render(renderer):
|
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 = {}
|
uniforms = {}
|
||||||
shade_factor = 1.0
|
shade_factor = 1.0
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@ from UM.Math.Matrix import Matrix
|
||||||
from UM.Math.Vector import Vector
|
from UM.Math.Vector import Vector
|
||||||
from UM.Mesh.MeshBuilder import MeshBuilder
|
from UM.Mesh.MeshBuilder import MeshBuilder
|
||||||
from UM.Mesh.MeshReader import MeshReader
|
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
|
MYPY = False
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -409,6 +409,7 @@ UM.MainWindow
|
||||||
{
|
{
|
||||||
id: objectsList;
|
id: objectsList;
|
||||||
visible: false;
|
visible: false;
|
||||||
|
//z: -10;
|
||||||
anchors
|
anchors
|
||||||
{
|
{
|
||||||
top: objectsButton.top;
|
top: objectsButton.top;
|
||||||
|
|
|
@ -55,7 +55,7 @@ Rectangle
|
||||||
//anchors.right: parent.right
|
//anchors.right: parent.right
|
||||||
width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30
|
width: parent.width - 2 * UM.Theme.getSize("default_margin").width - 30
|
||||||
text: Cura.ObjectManager.getItem(index).name;
|
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
|
elide: Text.ElideRight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ Rectangle
|
||||||
topMargin: UM.Theme.getSize("default_margin").height;
|
topMargin: UM.Theme.getSize("default_margin").height;
|
||||||
left: parent.left;
|
left: parent.left;
|
||||||
leftMargin: UM.Theme.getSize("default_margin").height;
|
leftMargin: UM.Theme.getSize("default_margin").height;
|
||||||
bottom: buildPlateSelection.top;
|
bottom: filterBuildPlateCheckbox.top;
|
||||||
bottomMargin: UM.Theme.getSize("default_margin").height;
|
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
|
ListModel
|
||||||
{
|
{
|
||||||
id: buildPlatesModel
|
id: buildPlatesModel
|
||||||
|
|
|
@ -451,6 +451,20 @@ UM.PreferencesPage
|
||||||
text: catalog.i18nc("@label","Opening and saving files")
|
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 {
|
UM.TooltipArea {
|
||||||
width: childrenRect.width
|
width: childrenRect.width
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue