|
@@ -1,4 +1,5 @@
|
|
|
# Copyright (c) 2017 Ultimaker B.V.
|
|
|
+# Copyright (c) 2017 Ultimaker B.V.
|
|
|
# Cura is released under the terms of the LGPLv3 or higher.
|
|
|
from PyQt5.QtNetwork import QLocalServer
|
|
|
from PyQt5.QtNetwork import QLocalSocket
|
|
@@ -16,7 +17,6 @@ from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator
|
|
|
from UM.Mesh.ReadMeshJob import ReadMeshJob
|
|
|
from UM.Logger import Logger
|
|
|
from UM.Preferences import Preferences
|
|
|
-from UM.SaveFile import SaveFile
|
|
|
from UM.Scene.Selection import Selection
|
|
|
from UM.Scene.GroupDecorator import GroupDecorator
|
|
|
from UM.Settings.ContainerStack import ContainerStack
|
|
@@ -32,15 +32,19 @@ 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
|
|
|
-from cura.SetParentOperation import SetParentOperation
|
|
|
-from cura.SliceableObjectDecorator import SliceableObjectDecorator
|
|
|
-from cura.BlockSlicingDecorator import BlockSlicingDecorator
|
|
|
-
|
|
|
-from cura.ArrangeObjectsJob import ArrangeObjectsJob
|
|
|
+from cura.Arranging.Arrange import Arrange
|
|
|
+from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob
|
|
|
+from cura.Arranging.ArrangeObjectsAllBuildPlatesJob import ArrangeObjectsAllBuildPlatesJob
|
|
|
+from cura.Arranging.ShapeArray import ShapeArray
|
|
|
from cura.MultiplyObjectsJob import MultiplyObjectsJob
|
|
|
+from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
|
|
|
+from cura.Operations.SetParentOperation import SetParentOperation
|
|
|
+from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
|
|
|
+from cura.Scene.BlockSlicingDecorator import BlockSlicingDecorator
|
|
|
+from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
|
|
|
+from cura.Scene.CuraSceneNode import CuraSceneNode
|
|
|
+
|
|
|
+from cura.CuraSceneController import CuraSceneController
|
|
|
|
|
|
from UM.Settings.SettingDefinition import SettingDefinition, DefinitionPropertyType
|
|
|
from UM.Settings.ContainerRegistry import ContainerRegistry
|
|
@@ -53,12 +57,13 @@ from cura.Settings.SettingInheritanceManager import SettingInheritanceManager
|
|
|
from cura.Settings.UserProfilesModel import UserProfilesModel
|
|
|
from cura.Settings.SimpleModeSettingsManager import SimpleModeSettingsManager
|
|
|
|
|
|
+
|
|
|
from . import PlatformPhysics
|
|
|
from . import BuildVolume
|
|
|
from . import CameraAnimation
|
|
|
from . import PrintInformation
|
|
|
from . import CuraActions
|
|
|
-from . import ZOffsetDecorator
|
|
|
+from cura.Scene import ZOffsetDecorator
|
|
|
from . import CuraSplashScreen
|
|
|
from . import CameraImageProvider
|
|
|
from . import MachineActionManager
|
|
@@ -72,8 +77,9 @@ from cura.Settings.ContainerSettingsModel import ContainerSettingsModel
|
|
|
from cura.Settings.MaterialSettingsVisibilityHandler import MaterialSettingsVisibilityHandler
|
|
|
from cura.Settings.QualitySettingsModel import QualitySettingsModel
|
|
|
from cura.Settings.ContainerManager import ContainerManager
|
|
|
-from cura.Settings.GlobalStack import GlobalStack
|
|
|
-from cura.Settings.ExtruderStack import ExtruderStack
|
|
|
+
|
|
|
+from cura.ObjectsModel import ObjectsModel
|
|
|
+from cura.BuildPlateModel import BuildPlateModel
|
|
|
|
|
|
from PyQt5.QtCore import QUrl, pyqtSignal, pyqtProperty, QEvent, Q_ENUMS
|
|
|
from UM.FlameProfiler import pyqtSlot
|
|
@@ -85,7 +91,6 @@ import sys
|
|
|
import os.path
|
|
|
import numpy
|
|
|
import copy
|
|
|
-import urllib.parse
|
|
|
import os
|
|
|
import argparse
|
|
|
import json
|
|
@@ -203,8 +208,11 @@ class CuraApplication(QtApplication):
|
|
|
self._machine_manager = None # This is initialized on demand.
|
|
|
self._extruder_manager = None
|
|
|
self._material_manager = None
|
|
|
+ self._object_manager = None
|
|
|
+ self._build_plate_model = None
|
|
|
self._setting_inheritance_manager = None
|
|
|
self._simple_mode_settings_manager = None
|
|
|
+ self._cura_scene_controller = None
|
|
|
|
|
|
self._additional_components = {} # Components to add to certain areas in the interface
|
|
|
|
|
@@ -311,11 +319,14 @@ 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/use_multi_build_plate", False)
|
|
|
|
|
|
preferences.addPreference("cura/currency", "€")
|
|
|
preferences.addPreference("cura/material_settings", "{}")
|
|
|
|
|
|
preferences.addPreference("view/invert_zoom", False)
|
|
|
+ preferences.addPreference("view/filter_current_build_plate", False)
|
|
|
preferences.addPreference("cura/sidebar_collapsed", False)
|
|
|
|
|
|
self._need_to_show_user_agreement = not Preferences.getInstance().getValue("general/accepted_user_agreement")
|
|
@@ -389,6 +400,8 @@ class CuraApplication(QtApplication):
|
|
|
|
|
|
self._plugin_registry.addSupportedPluginExtension("curaplugin", "Cura Plugin")
|
|
|
|
|
|
+ self.getCuraSceneController().setActiveBuildPlate(0) # Initialize
|
|
|
+
|
|
|
def _onEngineCreated(self):
|
|
|
self._engine.addImageProvider("camera", CameraImageProvider.CameraImageProvider())
|
|
|
|
|
@@ -684,8 +697,16 @@ class CuraApplication(QtApplication):
|
|
|
qmlRegisterSingletonType(ExtruderManager, "Cura", 1, 0, "ExtruderManager", self.getExtruderManager)
|
|
|
qmlRegisterSingletonType(MachineManager, "Cura", 1, 0, "MachineManager", self.getMachineManager)
|
|
|
qmlRegisterSingletonType(MaterialManager, "Cura", 1, 0, "MaterialManager", self.getMaterialManager)
|
|
|
- qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager", self.getSettingInheritanceManager)
|
|
|
- qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager", self.getSimpleModeSettingsManager)
|
|
|
+
|
|
|
+ qmlRegisterSingletonType(SettingInheritanceManager, "Cura", 1, 0, "SettingInheritanceManager",
|
|
|
+ self.getSettingInheritanceManager)
|
|
|
+ qmlRegisterSingletonType(SimpleModeSettingsManager, "Cura", 1, 2, "SimpleModeSettingsManager",
|
|
|
+ self.getSimpleModeSettingsManager)
|
|
|
+
|
|
|
+ qmlRegisterSingletonType(ObjectsModel, "Cura", 1, 2, "ObjectsModel", self.getObjectsModel)
|
|
|
+ qmlRegisterSingletonType(BuildPlateModel, "Cura", 1, 2, "BuildPlateModel", self.getBuildPlateModel)
|
|
|
+ qmlRegisterSingletonType(CuraSceneController, "Cura", 1, 2, "SceneController", self.getCuraSceneController)
|
|
|
+
|
|
|
qmlRegisterSingletonType(MachineActionManager.MachineActionManager, "Cura", 1, 0, "MachineActionManager", self.getMachineActionManager)
|
|
|
|
|
|
self.setMainQml(Resources.getPath(self.ResourceTypes.QmlFiles, "Cura.qml"))
|
|
@@ -723,6 +744,22 @@ class CuraApplication(QtApplication):
|
|
|
self._material_manager = MaterialManager.createMaterialManager()
|
|
|
return self._material_manager
|
|
|
|
|
|
+ def getObjectsModel(self, *args):
|
|
|
+ if self._object_manager is None:
|
|
|
+ self._object_manager = ObjectsModel.createObjectsModel()
|
|
|
+ return self._object_manager
|
|
|
+
|
|
|
+ def getBuildPlateModel(self, *args):
|
|
|
+ if self._build_plate_model is None:
|
|
|
+ self._build_plate_model = BuildPlateModel.createBuildPlateModel()
|
|
|
+
|
|
|
+ return self._build_plate_model
|
|
|
+
|
|
|
+ def getCuraSceneController(self, *args):
|
|
|
+ if self._cura_scene_controller is None:
|
|
|
+ self._cura_scene_controller = CuraSceneController.createCuraSceneController()
|
|
|
+ return self._cura_scene_controller
|
|
|
+
|
|
|
def getSettingInheritanceManager(self, *args):
|
|
|
if self._setting_inheritance_manager is None:
|
|
|
self._setting_inheritance_manager = SettingInheritanceManager.createSettingInheritanceManager()
|
|
@@ -858,7 +895,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
|
|
@@ -975,7 +1012,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.
|
|
@@ -983,6 +1020,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.
|
|
@@ -994,7 +1034,7 @@ class CuraApplication(QtApplication):
|
|
|
|
|
|
nodes = []
|
|
|
for node in DepthFirstIterator(self.getController().getScene().getRoot()):
|
|
|
- if type(node) is not SceneNode:
|
|
|
+ if type(node) not in {SceneNode, CuraSceneNode}:
|
|
|
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.
|
|
@@ -1010,13 +1050,15 @@ class CuraApplication(QtApplication):
|
|
|
op.push()
|
|
|
Selection.clear()
|
|
|
|
|
|
- ## Reset all translation on nodes with mesh data.
|
|
|
+ self.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
|
|
|
+
|
|
|
+ ## Reset all translation on nodes with mesh data.
|
|
|
@pyqtSlot()
|
|
|
def resetAllTranslation(self):
|
|
|
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.
|
|
@@ -1044,13 +1086,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)
|
|
|
|
|
@@ -1068,20 +1110,44 @@ class CuraApplication(QtApplication):
|
|
|
|
|
|
## Arrange all objects.
|
|
|
@pyqtSlot()
|
|
|
- def arrangeAll(self):
|
|
|
+ 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.isSelectable():
|
|
|
+ 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.getCuraSceneController().setActiveBuildPlate(0) # Select first build plate
|
|
|
+
|
|
|
+ # Single build plate
|
|
|
+ @pyqtSlot()
|
|
|
+ def arrangeAll(self):
|
|
|
+ nodes = []
|
|
|
+ active_build_plate = self.getBuildPlateModel().activeBuildPlate
|
|
|
+ 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.
|
|
|
+ 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
|
|
|
+ if not node.callDecoration("isSliceable"):
|
|
|
+ continue # i.e. node with layer data
|
|
|
+ if node.callDecoration("getBuildPlateNumber") == active_build_plate:
|
|
|
+ # 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)
|
|
|
self.arrange(nodes, fixed_nodes = [])
|
|
|
|
|
|
## Arrange Selection
|
|
@@ -1092,7 +1158,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.
|
|
@@ -1100,6 +1166,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)
|
|
@@ -1118,7 +1186,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)
|
|
@@ -1135,7 +1203,7 @@ class CuraApplication(QtApplication):
|
|
|
job.start()
|
|
|
else:
|
|
|
Logger.log("w", "Unable to reload data because we don't have a filename.")
|
|
|
-
|
|
|
+
|
|
|
## Get logging data of the backend engine
|
|
|
# \returns \type{string} Logging data
|
|
|
@pyqtSlot(result = str)
|
|
@@ -1209,10 +1277,11 @@ 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())
|
|
|
+ group_node.addDecorator(BuildPlateDecorator(self.getBuildPlateModel().activeBuildPlate))
|
|
|
group_node.setParent(self.getController().getScene().getRoot())
|
|
|
group_node.setSelectable(True)
|
|
|
center = Selection.getSelectionCenter()
|
|
@@ -1357,8 +1426,15 @@ class CuraApplication(QtApplication):
|
|
|
min_offset = 8
|
|
|
|
|
|
self.fileLoaded.emit(filename)
|
|
|
+ arrange_objects_on_load = (
|
|
|
+ not Preferences.getInstance().getValue("cura/use_multi_build_plate") or
|
|
|
+ Preferences.getInstance().getValue("cura/arrange_objects_on_load"))
|
|
|
+ target_build_plate = self.getBuildPlateModel().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))
|
|
|
|
|
@@ -1385,21 +1461,23 @@ class CuraApplication(QtApplication):
|
|
|
if not child.getDecorator(ConvexHullDecorator):
|
|
|
child.addDecorator(ConvexHullDecorator())
|
|
|
|
|
|
- if node.callDecoration("isSliceable"):
|
|
|
- # Only check position if it's not already blatantly obvious that it won't fit.
|
|
|
- if node.getBoundingBox() is None or self._volume.getBoundingBox() is None or node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
|
|
- # Find node location
|
|
|
- offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = min_offset)
|
|
|
-
|
|
|
- # If a model is to small then it will not contain any points
|
|
|
- if offset_shape_arr is None and hull_shape_arr is None:
|
|
|
- Message(self._i18n_catalog.i18nc("@info:status", "The selected model was too small to load."),
|
|
|
- title=self._i18n_catalog.i18nc("@info:title", "Warning")
|
|
|
- ).show()
|
|
|
- return
|
|
|
-
|
|
|
- # 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)
|
|
|
+ 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() is None or self._volume.getBoundingBox() is None or node.getBoundingBox().width < self._volume.getBoundingBox().width or node.getBoundingBox().depth < self._volume.getBoundingBox().depth:
|
|
|
+ # Find node location
|
|
|
+ offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = min_offset)
|
|
|
+
|
|
|
+ # If a model is to small then it will not contain any points
|
|
|
+ if offset_shape_arr is None and hull_shape_arr is None:
|
|
|
+ Message(self._i18n_catalog.i18nc("@info:status", "The selected model was too small to load."),
|
|
|
+ title=self._i18n_catalog.i18nc("@info:title", "Warning")).show()
|
|
|
+ return
|
|
|
+
|
|
|
+ # 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()
|