From 55e5cd8982e266a8b28b062fb113e150aaef815d Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Mon, 26 Feb 2024 14:58:53 +0100 Subject: [PATCH 1/6] adding dropto buildplate drop per model in 3mf CURA-10542 --- cura/PlatformPhysics.py | 19 ++++++++++++------- cura/Scene/CuraSceneNode.py | 6 ++++++ plugins/3MFReader/ThreeMFReader.py | 4 ++++ plugins/3MFReader/WorkspaceDialog.qml | 1 + plugins/3MFWriter/ThreeMFWriter.py | 1 + 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 6a26190a56..306f6a837a 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -39,7 +39,7 @@ class PlatformPhysics: Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False) Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", False) - self._app_per_model_drop = Application.getInstance().getPreferences().getValue("physics/automatic_drop_down") + self._app_per_model_drop = None def getAppPerModelDropDown(self): return self._app_per_model_drop @@ -78,6 +78,14 @@ class PlatformPhysics: # We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A. # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve. random.shuffle(nodes) + drop_down = False + # drop down in case nodes are asked to drop down from the user from workspaceDialog while opening 3mf + if self._app_per_model_drop and (self._app_per_model_drop != app_automatic_drop_down): + drop_down = True + # drop down in case the user has selected automated drop down preference for 3mf opening + if self._app_per_model_drop and app_automatic_drop_down: + drop_down= True + for node in nodes: if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None: @@ -88,12 +96,9 @@ class PlatformPhysics: # Move it downwards if bottom is above platform move_vector = Vector() - # if per model drop is different then app_automatic_drop, in case of 3mf loading when user changes this setting for that model - if (self._app_per_model_drop != app_automatic_drop_down): - node.setSetting(SceneNodeSettings.AutoDropDown, self._app_per_model_drop) - if node.getSetting(SceneNodeSettings.AutoDropDown, self._app_per_model_drop) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): #If an object is grouped, don't move it down + if (node.getSetting(SceneNodeSettings.AutoDropDown, app_automatic_drop_down) or drop_down) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0 - move_vector = move_vector.set(y = -bbox.bottom + z_offset) + move_vector = move_vector.set(y=-bbox.bottom + z_offset) # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator) and not node.callDecoration("isNonPrintingMesh") and node.callDecoration("getLayerData") is None: @@ -180,7 +185,7 @@ class PlatformPhysics: op.push() # setting this drop to model same as app_automatic_drop_down - self._app_per_model_drop = app_automatic_drop_down + self._app_per_model_drop = None # After moving, we have to evaluate the boundary checks for nodes build_volume.updateNodeBoundaryCheck() diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 9b412c7d4f..5a47fa0f0b 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -11,6 +11,7 @@ from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNodeDecorator import SceneNodeDecorator # To cast the deepcopy of every decorator back to SceneNodeDecorator. import cura.CuraApplication # To get the build plate. +from UM.Scene.SceneNodeSettings import SceneNodeSettings from cura.Settings.ExtruderStack import ExtruderStack # For typing. from cura.Settings.SettingOverrideDecorator import SettingOverrideDecorator # For per-object settings. @@ -26,6 +27,7 @@ class CuraSceneNode(SceneNode): self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled self._outside_buildarea = False self._print_order = 0 + self._drop_down = Application.getInstance().getPreferences().getValue("physics/automatic_drop_down") def setOutsideBuildArea(self, new_value: bool) -> None: self._outside_buildarea = new_value @@ -41,6 +43,10 @@ class CuraSceneNode(SceneNode): def isOutsideBuildArea(self) -> bool: return self._outside_buildarea or self.callDecoration("getBuildPlateNumber") < 0 + @property + def isDropDownEnabled(self) ->bool: + return self.getSetting(SceneNodeSettings.AutoDropDown, self._drop_down) + def isVisible(self) -> bool: return super().isVisible() and self.callDecoration("getBuildPlateNumber") == cura.CuraApplication.CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate diff --git a/plugins/3MFReader/ThreeMFReader.py b/plugins/3MFReader/ThreeMFReader.py index ac94282136..99852d84ce 100755 --- a/plugins/3MFReader/ThreeMFReader.py +++ b/plugins/3MFReader/ThreeMFReader.py @@ -16,6 +16,7 @@ from UM.Mesh.MeshReader import MeshReader from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType from UM.Scene.GroupDecorator import GroupDecorator from UM.Scene.SceneNode import SceneNode # For typing. +from UM.Scene.SceneNodeSettings import SceneNodeSettings from cura.CuraApplication import CuraApplication from cura.Machines.ContainerTree import ContainerTree from cura.Scene.BuildPlateDecorator import BuildPlateDecorator @@ -180,6 +181,9 @@ class ThreeMFReader(MeshReader): if key == "print_order": um_node.printOrder = int(setting_value) continue + if key =="drop_to_buildplate": + um_node.setSetting(SceneNodeSettings.AutoDropDown, eval(setting_value)) + continue if key in known_setting_keys: setting_container.setProperty(key, "value", setting_value) else: diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index 334317e0dc..0cef71fd42 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -366,6 +366,7 @@ UM.Dialog } function reloadValue() { + manager.manager.setDropToBuildPlateForModel(checkDropModels.checked) checkDropModels.checked = UM.Preferences.getValue("physics/automatic_drop_down") } } diff --git a/plugins/3MFWriter/ThreeMFWriter.py b/plugins/3MFWriter/ThreeMFWriter.py index 6fda1742f8..c321505643 100644 --- a/plugins/3MFWriter/ThreeMFWriter.py +++ b/plugins/3MFWriter/ThreeMFWriter.py @@ -155,6 +155,7 @@ class ThreeMFWriter(MeshWriter): if isinstance(um_node, CuraSceneNode): savitar_node.setSetting("cura:print_order", str(um_node.printOrder)) + savitar_node.setSetting("cura:drop_to_buildplate", str(um_node.isDropDownEnabled)) # Store the metadata. for key, value in um_node.metadata.items(): From fb233d8b16460e3d5b38167a45d834e005dc0920 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Mon, 26 Feb 2024 14:59:48 +0100 Subject: [PATCH 2/6] preference setting of "drop+to_buildplate" preseve from previous versions CURA-10542 --- cura/PlatformPhysics.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 306f6a837a..74fe05927d 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -38,7 +38,7 @@ class PlatformPhysics: self._minimum_gap = 2 # It is a minimum distance (in mm) between two models, applicable for small models Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False) - Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", False) + Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", True) self._app_per_model_drop = None def getAppPerModelDropDown(self): From b5b9dd0e572378e8b6376994cd60e3a8ee5e6089 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Mon, 26 Feb 2024 15:51:36 +0100 Subject: [PATCH 3/6] fixing unit-test CURA-10542 --- cura/Scene/CuraSceneNode.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cura/Scene/CuraSceneNode.py b/cura/Scene/CuraSceneNode.py index 5a47fa0f0b..65b7c1e593 100644 --- a/cura/Scene/CuraSceneNode.py +++ b/cura/Scene/CuraSceneNode.py @@ -27,7 +27,6 @@ class CuraSceneNode(SceneNode): self.addDecorator(SettingOverrideDecorator()) # Now we always have a getActiveExtruderPosition, unless explicitly disabled self._outside_buildarea = False self._print_order = 0 - self._drop_down = Application.getInstance().getPreferences().getValue("physics/automatic_drop_down") def setOutsideBuildArea(self, new_value: bool) -> None: self._outside_buildarea = new_value @@ -45,7 +44,7 @@ class CuraSceneNode(SceneNode): @property def isDropDownEnabled(self) ->bool: - return self.getSetting(SceneNodeSettings.AutoDropDown, self._drop_down) + return self.getSetting(SceneNodeSettings.AutoDropDown, Application.getInstance().getPreferences().getValue("physics/automatic_drop_down")) def isVisible(self) -> bool: return super().isVisible() and self.callDecoration("getBuildPlateNumber") == cura.CuraApplication.CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate From 59d6593c176a8fcc252eb45b2eb82e4ed3e11dc0 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Tue, 27 Feb 2024 09:57:39 +0100 Subject: [PATCH 4/6] fixing reloadvalue manager CURA-10542 --- plugins/3MFReader/WorkspaceDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index 0cef71fd42..dbd6356e94 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -366,7 +366,7 @@ UM.Dialog } function reloadValue() { - manager.manager.setDropToBuildPlateForModel(checkDropModels.checked) + manager.setDropToBuildPlateForModel(checkDropModels.checked) checkDropModels.checked = UM.Preferences.getValue("physics/automatic_drop_down") } } From 1338a562efca7169a3dc38c5aec76180dbe3aa7a Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Tue, 27 Feb 2024 13:45:10 +0100 Subject: [PATCH 5/6] added drop models to buildplate as a right click option Also shortcut: ctrl+B CURA-10542 --- cura/CuraApplication.py | 6 +++--- cura/PlatformPhysics.py | 16 ++++------------ plugins/3MFReader/WorkspaceDialog.py | 5 ----- plugins/3MFReader/WorkspaceDialog.qml | 21 --------------------- resources/qml/Actions.qml | 10 +++++++++- resources/qml/Menus/ContextMenu.qml | 1 + resources/qml/Menus/EditMenu.qml | 1 + resources/qml/Preferences/GeneralPage.qml | 1 - 8 files changed, 18 insertions(+), 43 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 6132d8ab36..680baefdb3 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1085,9 +1085,9 @@ class CuraApplication(QtApplication): def getTextManager(self, *args) -> "TextManager": return self._text_manager - @pyqtSlot(bool) - def getWorkplaceDropToBuildplate(self, drop_to_build_plate: bool) ->None: - return self._physics.setAppPerModelDropDown(drop_to_build_plate) + @pyqtSlot() + def setWorkplaceDropToBuildplate(self): + return self._physics.setAppPerModelDropDown() def getCuraFormulaFunctions(self, *args) -> "CuraFormulaFunctions": if self._cura_formula_functions is None: diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 74fe05927d..1f86258b42 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -41,11 +41,9 @@ class PlatformPhysics: Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", True) self._app_per_model_drop = None - def getAppPerModelDropDown(self): - return self._app_per_model_drop - - def setAppPerModelDropDown(self, drop_to_buildplate): - self._app_per_model_drop = drop_to_buildplate + def setAppPerModelDropDown(self): + self._app_per_model_drop = True + self._onChangeTimerFinished() def _onSceneChanged(self, source): if not source.callDecoration("isSliceable"): @@ -79,13 +77,7 @@ class PlatformPhysics: # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve. random.shuffle(nodes) drop_down = False - # drop down in case nodes are asked to drop down from the user from workspaceDialog while opening 3mf - if self._app_per_model_drop and (self._app_per_model_drop != app_automatic_drop_down): - drop_down = True - # drop down in case the user has selected automated drop down preference for 3mf opening - if self._app_per_model_drop and app_automatic_drop_down: - drop_down= True - + if self._app_per_model_drop == True: drop_down = True for node in nodes: if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None: diff --git a/plugins/3MFReader/WorkspaceDialog.py b/plugins/3MFReader/WorkspaceDialog.py index c0ea950915..1fafcf59f5 100644 --- a/plugins/3MFReader/WorkspaceDialog.py +++ b/plugins/3MFReader/WorkspaceDialog.py @@ -353,11 +353,6 @@ class WorkspaceDialog(QObject): Application.getInstance().getBackend().close() - @pyqtSlot(bool) - def setDropToBuildPlateForModel(self, drop_to_buildplate: bool) -> None: - CuraApplication.getInstance().getWorkplaceDropToBuildplate(drop_to_buildplate) - - def setMaterialConflict(self, material_conflict: bool) -> None: if self._has_material_conflict != material_conflict: self._has_material_conflict = material_conflict diff --git a/plugins/3MFReader/WorkspaceDialog.qml b/plugins/3MFReader/WorkspaceDialog.qml index dbd6356e94..8d06b32e14 100644 --- a/plugins/3MFReader/WorkspaceDialog.qml +++ b/plugins/3MFReader/WorkspaceDialog.qml @@ -351,26 +351,6 @@ UM.Dialog } } - Row - { - id: dropToBuildPlate - width: parent.width - height: childrenRect.height - spacing: UM.Theme.getSize("default_margin").width - UM.CheckBox - { - id: checkDropModels - text: catalog.i18nc("@text:window", "Drop models to buildplate") - checked: UM.Preferences.getValue("physics/automatic_drop_down") - onCheckedChanged: manager.setDropToBuildPlateForModel(checked) - } - function reloadValue() - { - manager.setDropToBuildPlateForModel(checkDropModels.checked) - checkDropModels.checked = UM.Preferences.getValue("physics/automatic_drop_down") - } - } - Row { id: clearBuildPlateWarning @@ -493,7 +473,6 @@ UM.Dialog materialSection.reloadValues() profileSection.reloadValues() printerSection.reloadValues() - dropToBuildPlate.reloadValue() } } } diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 8fe4a11847..7acf39ecb2 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -39,7 +39,7 @@ Item property alias printObjectAfterNext: printObjectAfterNextAction property alias multiplyObject: multiplyObjectAction - + property alias dropAll: dropAllAction property alias selectAll: selectAllAction property alias deleteAll: deleteAllAction property alias reloadAll: reloadAllAction @@ -490,6 +490,14 @@ Item shortcut: "Shift+Ctrl+R" } + Action + { + id: dropAllAction + text: catalog.i18nc("@action:inmenu menubar:edit","Drop All Models to buildplate") + shortcut: "Ctrl+B" + onTriggered: CuraApplication.setWorkplaceDropToBuildplate() + } + Action { id: resetAllTranslationAction diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 1b32c24254..1ab0a1332e 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -71,6 +71,7 @@ Cura.Menu Cura.MenuItem { action: Cura.Actions.reloadAll } Cura.MenuItem { action: Cura.Actions.resetAllTranslation } Cura.MenuItem { action: Cura.Actions.resetAll } + Cura.MenuItem { action: Cura.Actions.dropAll } // Group actions Cura.MenuSeparator {} diff --git a/resources/qml/Menus/EditMenu.qml b/resources/qml/Menus/EditMenu.qml index 728b3b212b..fdab70360e 100644 --- a/resources/qml/Menus/EditMenu.qml +++ b/resources/qml/Menus/EditMenu.qml @@ -21,6 +21,7 @@ Cura.Menu Cura.MenuItem { action: Cura.Actions.deleteAll } Cura.MenuItem { action: Cura.Actions.resetAllTranslation } Cura.MenuItem { action: Cura.Actions.resetAll } + Cura.MenuItem { action: Cura.Actions.dropAll } Cura.MenuSeparator { } Cura.MenuItem { action: Cura.Actions.groupObjects } Cura.MenuItem { action: Cura.Actions.mergeObjects } diff --git a/resources/qml/Preferences/GeneralPage.qml b/resources/qml/Preferences/GeneralPage.qml index d87ecac0b3..0f50f169ef 100644 --- a/resources/qml/Preferences/GeneralPage.qml +++ b/resources/qml/Preferences/GeneralPage.qml @@ -513,7 +513,6 @@ UM.PreferencesPage onCheckedChanged: { UM.Preferences.setValue("physics/automatic_drop_down", checked) - CuraApplication.getWorkplaceDropToBuildplate(checked) } } } From 586590605774c8bca7a08a71f62571e8fac80332 Mon Sep 17 00:00:00 2001 From: Saumya Jain Date: Tue, 27 Feb 2024 13:52:59 +0100 Subject: [PATCH 6/6] function Name change CURA-10542 --- cura/CuraApplication.py | 2 +- cura/PlatformPhysics.py | 13 +++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 680baefdb3..c32017371f 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1087,7 +1087,7 @@ class CuraApplication(QtApplication): @pyqtSlot() def setWorkplaceDropToBuildplate(self): - return self._physics.setAppPerModelDropDown() + return self._physics.setAppAllModelDropDown() def getCuraFormulaFunctions(self, *args) -> "CuraFormulaFunctions": if self._cura_formula_functions is None: diff --git a/cura/PlatformPhysics.py b/cura/PlatformPhysics.py index 1f86258b42..c61f78d86e 100755 --- a/cura/PlatformPhysics.py +++ b/cura/PlatformPhysics.py @@ -39,10 +39,10 @@ class PlatformPhysics: Application.getInstance().getPreferences().addPreference("physics/automatic_push_free", False) Application.getInstance().getPreferences().addPreference("physics/automatic_drop_down", True) - self._app_per_model_drop = None + self._app_all_model_drop = False - def setAppPerModelDropDown(self): - self._app_per_model_drop = True + def setAppAllModelDropDown(self): + self._app_all_model_drop = True self._onChangeTimerFinished() def _onSceneChanged(self, source): @@ -76,9 +76,6 @@ class PlatformPhysics: # We try to shuffle all the nodes to prevent "locked" situations, where iteration B inverts iteration A. # By shuffling the order of the nodes, this might happen a few times, but at some point it will resolve. random.shuffle(nodes) - drop_down = False - if self._app_per_model_drop == True: drop_down = True - for node in nodes: if node is root or not isinstance(node, SceneNode) or node.getBoundingBox() is None: continue @@ -88,7 +85,7 @@ class PlatformPhysics: # Move it downwards if bottom is above platform move_vector = Vector() - if (node.getSetting(SceneNodeSettings.AutoDropDown, app_automatic_drop_down) or drop_down) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): + if (node.getSetting(SceneNodeSettings.AutoDropDown, app_automatic_drop_down) or self._app_all_model_drop) and not (node.getParent() and node.getParent().callDecoration("isGroup") or node.getParent() != root) and node.isEnabled(): z_offset = node.callDecoration("getZOffset") if node.getDecorator(ZOffsetDecorator.ZOffsetDecorator) else 0 move_vector = move_vector.set(y=-bbox.bottom + z_offset) @@ -177,7 +174,7 @@ class PlatformPhysics: op.push() # setting this drop to model same as app_automatic_drop_down - self._app_per_model_drop = None + self._app_all_model_drop = False # After moving, we have to evaluate the boundary checks for nodes build_volume.updateNodeBoundaryCheck()