From 4f9d041ae84206c4ad3f7d2c6f01912965d57890 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 11 May 2023 16:14:38 +0200 Subject: [PATCH 01/30] Add rotation lock in arrange and multiply objects Add rotation lock in - context menu item arrange and - checkbox in multiply objects dialog CURA-7951 --- cura/Arranging/ArrangeObjectsJob.py | 7 +++-- cura/Arranging/Nest2DArrange.py | 36 ++++++++++++++++------- cura/CuraActions.py | 10 ++++--- cura/CuraApplication.py | 11 ++++---- cura/MultiplyObjectsJob.py | 14 +++++---- resources/qml/Actions.qml | 20 +++++++++++-- resources/qml/Menus/ContextMenu.qml | 44 ++++++++++++++++++----------- 7 files changed, 97 insertions(+), 45 deletions(-) diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index 6ba6717191..f938b44d1f 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -14,11 +14,13 @@ i18n_catalog = i18nCatalog("cura") class ArrangeObjectsJob(Job): - def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8) -> None: + def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset=8, + lock_rotation: bool = False) -> None: super().__init__() self._nodes = nodes self._fixed_nodes = fixed_nodes self._min_offset = min_offset + self._lock_rotation = lock_rotation def run(self): found_solution_for_all = False @@ -30,7 +32,8 @@ class ArrangeObjectsJob(Job): status_message.show() try: - found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, + lock_rotation=self._lock_rotation) except: # If the thread crashes, the message should still close Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") diff --git a/cura/Arranging/Nest2DArrange.py b/cura/Arranging/Nest2DArrange.py index 21427f1194..8921c9ede2 100644 --- a/cura/Arranging/Nest2DArrange.py +++ b/cura/Arranging/Nest2DArrange.py @@ -22,7 +22,13 @@ if TYPE_CHECKING: from cura.BuildVolume import BuildVolume -def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: Optional[List["SceneNode"]] = None, factor = 10000) -> Tuple[bool, List[Item]]: +def findNodePlacement( + nodes_to_arrange: List["SceneNode"], + build_volume: "BuildVolume", + fixed_nodes: Optional[List["SceneNode"]] = None, + factor: int = 10000, + lock_rotation: bool = False +) -> Tuple[bool, List[Item]]: """ Find placement for a set of scene nodes, but don't actually move them just yet. :param nodes_to_arrange: The list of nodes that need to be moved. @@ -30,6 +36,7 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV :param fixed_nodes: List of nods that should not be moved, but should be used when deciding where the others nodes are placed. :param factor: The library that we use is int based. This factor defines how accurate we want it to be. + :param lock_rotation: If set to true the orientation of the object will remain the same :return: tuple (found_solution_for_all, node_items) WHERE @@ -100,6 +107,8 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV config = NfpConfig() config.accuracy = 1.0 config.alignment = NfpConfig.Alignment.DONT_ALIGN + if lock_rotation: + config.rotations = [0.0] num_bins = nest(node_items, build_plate_bounding_box, spacing, config) @@ -114,10 +123,12 @@ def findNodePlacement(nodes_to_arrange: List["SceneNode"], build_volume: "BuildV def createGroupOperationForArrange(nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: Optional[List["SceneNode"]] = None, - factor = 10000, - add_new_nodes_in_scene: bool = False) -> Tuple[GroupedOperation, int]: + factor: int = 10000, + add_new_nodes_in_scene: bool = False, + lock_rotation: bool = False) -> Tuple[GroupedOperation, int]: scene_root = Application.getInstance().getController().getScene().getRoot() - found_solution_for_all, node_items = findNodePlacement(nodes_to_arrange, build_volume, fixed_nodes, factor) + found_solution_for_all, node_items = findNodePlacement(nodes_to_arrange, build_volume, fixed_nodes, factor, + lock_rotation) not_fit_count = 0 grouped_operation = GroupedOperation() @@ -141,11 +152,14 @@ def createGroupOperationForArrange(nodes_to_arrange: List["SceneNode"], return grouped_operation, not_fit_count -def arrange(nodes_to_arrange: List["SceneNode"], - build_volume: "BuildVolume", - fixed_nodes: Optional[List["SceneNode"]] = None, - factor = 10000, - add_new_nodes_in_scene: bool = False) -> bool: +def arrange( + nodes_to_arrange: List["SceneNode"], + build_volume: "BuildVolume", + fixed_nodes: Optional[List["SceneNode"]] = None, + factor=10000, + add_new_nodes_in_scene: bool = False, + lock_rotation: bool = False +) -> bool: """ Find placement for a set of scene nodes, and move them by using a single grouped operation. :param nodes_to_arrange: The list of nodes that need to be moved. @@ -154,10 +168,12 @@ def arrange(nodes_to_arrange: List["SceneNode"], are placed. :param factor: The library that we use is int based. This factor defines how accuracte we want it to be. :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations + :param lock_rotation: If set to true the orientation of the object will remain the same :return: found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects """ - grouped_operation, not_fit_count = createGroupOperationForArrange(nodes_to_arrange, build_volume, fixed_nodes, factor, add_new_nodes_in_scene) + grouped_operation, not_fit_count = createGroupOperationForArrange(nodes_to_arrange, build_volume, fixed_nodes, + factor, add_new_nodes_in_scene, lock_rotation) grouped_operation.push() return not_fit_count == 0 diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 193803325f..fed57c8500 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -75,19 +75,21 @@ class CuraActions(QObject): center_y = 0 # Move the object so that it's bottom is on to of the buildplate - center_operation = TranslateOperation(current_node, Vector(0, center_y, 0), set_position = True) + center_operation = TranslateOperation(current_node, Vector(0, center_y, 0), set_position=True) operation.addOperation(center_operation) operation.push() - @pyqtSlot(int) - def multiplySelection(self, count: int) -> None: + @pyqtSlot(int, bool) + def multiplySelection(self, count: int, lock_rotation: bool) -> None: """Multiply all objects in the selection :param count: The number of times to multiply the selection. + :param lock_rotation: If set to true the orientation of the object will remain the same """ min_offset = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors - job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = max(min_offset, 8)) + job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset=max(min_offset, 8), + lock_rotation=lock_rotation) job.start() @pyqtSlot() diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 113e7b3ff4..15e7a60fcb 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1421,8 +1421,8 @@ class CuraApplication(QtApplication): op.push() # Single build plate - @pyqtSlot() - def arrangeAll(self) -> None: + @pyqtSlot(bool) + def arrangeAll(self, lock_rotation: bool) -> None: nodes_to_arrange = [] active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate locked_nodes = [] @@ -1452,17 +1452,18 @@ class CuraApplication(QtApplication): locked_nodes.append(node) else: nodes_to_arrange.append(node) - self.arrange(nodes_to_arrange, locked_nodes) + self.arrange(nodes_to_arrange, locked_nodes, lock_rotation) - def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode]) -> None: + def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], lock_rotation: bool = False) -> None: """Arrange a set of nodes given a set of fixed nodes :param nodes: nodes that we have to place :param fixed_nodes: nodes that are placed in the arranger before finding spots for nodes + :param lock_rotation: If set to true the orientation of the object will remain the same """ min_offset = self.getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors - job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset = max(min_offset, 8)) + job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset=max(min_offset, 8), lock_rotation=lock_rotation) job.start() @pyqtSlot() diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 1446ae687e..ff4f362b4c 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -20,11 +20,12 @@ i18n_catalog = i18nCatalog("cura") class MultiplyObjectsJob(Job): - def __init__(self, objects, count, min_offset = 8): + def __init__(self, objects, count: int, min_offset: int = 8, lock_rotation: bool = False): super().__init__() self._objects = objects - self._count = count - self._min_offset = min_offset + self._count: int = count + self._min_offset: int = min_offset + self._lock_rotation: bool = lock_rotation def run(self) -> None: status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime = 0, @@ -39,7 +40,7 @@ class MultiplyObjectsJob(Job): root = scene.getRoot() - processed_nodes = [] # type: List[SceneNode] + processed_nodes: List[SceneNode] = [] nodes = [] fixed_nodes = [] @@ -79,8 +80,9 @@ class MultiplyObjectsJob(Job): group_operation, not_fit_count = createGroupOperationForArrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes, - factor = 10000, - add_new_nodes_in_scene = True) + factor=10000, + add_new_nodes_in_scene=True, + lock_rotation=self._lock_rotation) found_solution_for_all = not_fit_count == 0 if nodes_to_add_without_arrange: diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 6cd75b51ac..eb9f1c7f21 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -41,7 +41,9 @@ Item property alias deleteAll: deleteAllAction property alias reloadAll: reloadAllAction property alias arrangeAll: arrangeAllAction + property alias arrangeAllLock: arrangeAllLockAction property alias arrangeSelection: arrangeSelectionAction + property alias arrangeSelectionLock: arrangeSelectionLockAction property alias resetAllTranslation: resetAllTranslationAction property alias resetAll: resetAllAction @@ -412,15 +414,29 @@ Item { id: arrangeAllAction text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models") - onTriggered: Printer.arrangeAll() + onTriggered: Printer.arrangeAll(false) shortcut: "Ctrl+R" } + Action + { + id: arrangeAllLockAction + text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models Without Rotation") + onTriggered: Printer.arrangeAll(true) + } + Action { id: arrangeSelectionAction text: catalog.i18nc("@action:inmenu menubar:edit","Arrange Selection") - onTriggered: Printer.arrangeSelection() + onTriggered: Printer.arrangeSelection(false) + } + + Action + { + id: arrangeSelectionLockAction + text: catalog.i18nc("@action:inmenu menubar:edit","Arrange Selection Without Rotation") + onTriggered: Printer.arrangeSelection(true) } Action diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 65f3409c8a..c327a37a2b 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -53,6 +53,7 @@ Cura.Menu Cura.MenuSeparator {} Cura.MenuItem { action: Cura.Actions.selectAll } Cura.MenuItem { action: Cura.Actions.arrangeAll } + Cura.MenuItem { action: Cura.Actions.arrangeAllLock } Cura.MenuItem { action: Cura.Actions.deleteAll } Cura.MenuItem { action: Cura.Actions.reloadAll } Cura.MenuItem { action: Cura.Actions.resetAllTranslation } @@ -96,7 +97,7 @@ Cura.Menu minimumWidth: UM.Theme.getSize("small_popup_dialog").width minimumHeight: UM.Theme.getSize("small_popup_dialog").height - onAccepted: CuraActions.multiplySelection(copiesField.value) + onAccepted: CuraActions.multiplySelection(copiesField.value, lockRotationField.checked) buttonSpacing: UM.Theme.getSize("thin_margin").width @@ -114,27 +115,38 @@ Cura.Menu } ] - Row + Column { - spacing: UM.Theme.getSize("default_margin").width + spacing: UM.Theme.getSize("default_margin").height - UM.Label + Row { - text: catalog.i18nc("@label", "Number of Copies") - anchors.verticalCenter: copiesField.verticalCenter - width: contentWidth - wrapMode: Text.NoWrap + spacing: UM.Theme.getSize("default_margin").width + + UM.Label + { + text: catalog.i18nc("@label", "Number of Copies") + anchors.verticalCenter: copiesField.verticalCenter + width: contentWidth + wrapMode: Text.NoWrap + } + + Cura.SpinBox + { + id: copiesField + editable: true + focus: true + from: 1 + to: 99 + width: 2 * UM.Theme.getSize("button").width + value: 1 + } } - Cura.SpinBox + UM.CheckBox { - id: copiesField - editable: true - focus: true - from: 1 - to: 99 - width: 2 * UM.Theme.getSize("button").width - value: 1 + id: lockRotationField + text: catalog.i18nc("@label", "Lock Rotation") } } } From d4c0f81719fae2af2117cb05b5c7aeab371f6654 Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 11 May 2023 14:20:25 +0000 Subject: [PATCH 02/30] update translations --- resources/i18n/cs_CZ/cura.po | 139 +++-- resources/i18n/cura.pot | 1065 +++++++++++++++++----------------- resources/i18n/de_DE/cura.po | 139 +++-- resources/i18n/es_ES/cura.po | 139 +++-- resources/i18n/fi_FI/cura.po | 139 +++-- resources/i18n/fr_FR/cura.po | 139 +++-- resources/i18n/hu_HU/cura.po | 139 +++-- resources/i18n/it_IT/cura.po | 139 +++-- resources/i18n/ja_JP/cura.po | 139 +++-- resources/i18n/ko_KR/cura.po | 139 +++-- resources/i18n/nl_NL/cura.po | 139 +++-- resources/i18n/pl_PL/cura.po | 139 +++-- resources/i18n/pt_BR/cura.po | 139 +++-- resources/i18n/pt_PT/cura.po | 139 +++-- resources/i18n/ru_RU/cura.po | 139 +++-- resources/i18n/tr_TR/cura.po | 139 +++-- resources/i18n/zh_CN/cura.po | 139 +++-- resources/i18n/zh_TW/cura.po | 139 +++-- 18 files changed, 1849 insertions(+), 1579 deletions(-) diff --git a/resources/i18n/cs_CZ/cura.po b/resources/i18n/cs_CZ/cura.po index f20c8c2949..29109ae325 100644 --- a/resources/i18n/cs_CZ/cura.po +++ b/resources/i18n/cs_CZ/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: 2023-02-16 20:28+0100\n" "Last-Translator: Miroslav Šustek \n" "Language-Team: DenyCZ \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Přihlášení selhalo" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Hledám nové umístění pro objekt" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Hledám umístění" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Nemohu najít lokaci na podložce pro všechny objekty" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Nemohu najít umístění" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Současně lze načíst pouze jeden soubor G-kódu. Přeskočen import {0}" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Varování" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Nelze otevřít žádný jiný soubor, když se načítá G kód. Přeskočen import {0}" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Vypočítáno" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Násobím a rozmisťuji objekty" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Umisťuji objekty" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Umisťuji objekt" @@ -3689,17 +3689,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Odstranit tiskárnu" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Tisk přes síť" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Tisk přes síť" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Připojeno přes síť" @@ -4241,227 +4241,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Zrušit" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Zobrazit online řešení problémů" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Přepnout zobrazení na celou obrazovku" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Ukončit zobrazení na celou obrazovku" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Vrátit" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Znovu" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Ukončit" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3D Pohled" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Přední pohled" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Pohled seshora" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Pohled zezdola" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Pohled z pravé strany" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Pohled z pravé strany" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Konfigurovat Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "Přidat t&iskárnu..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Spravovat &tiskárny..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Spravovat materiály..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Přidat více materiálů z Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Aktualizovat profil s aktuálními nastaveními/přepsáními" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "Smazat aktuální &změny" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Vytvořit profil z aktuálního nastavení/přepsání." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Spravovat profily..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Zobrazit online &dokumentaci" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Nahlásit &chybu" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Co je nového" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Více..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Smazat vybrané" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Centrovat vybrané" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Násobit vybrané" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Odstranit model" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "&Centerovat model na podložce" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "Sesk&upit modely" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Rozdělit modely" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "Spo&jit modely" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "Náso&bení modelu..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Vybrat všechny modely" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Vyčistit podložku" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Znovu načíst všechny modely" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Uspořádat všechny modely" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Uspořádat selekci" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Resetovat všechny pozice modelů" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Resetovat všechny transformace modelů" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Otevřít soubor(y)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Nový projekt..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Zobrazit složku s konfigurací" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Konfigurovat viditelnost nastavení..." @@ -5083,7 +5093,7 @@ msgstr[0] "Tisknout vybraný model pomocí:" msgstr[1] "Tisknout vybrané modely pomocí:" msgstr[2] "Tisknout vybrané modely pomocí:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" @@ -5091,11 +5101,16 @@ msgstr[0] "Násobit vybraný model" msgstr[1] "Násobit vybrané modele" msgstr[2] "Násobit vybrané modele" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Počet kopií" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/cura.pot b/resources/i18n/cura.pot index 9321ffa72b..3dc901e8e0 100644 --- a/resources/i18n/cura.pot +++ b/resources/i18n/cura.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,23 +23,23 @@ msgctxt "@info:title" msgid "Login failed" msgstr "" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "" -#: cura/Arranging/ArrangeObjectsJob.py:42 -#: cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 +#: cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "" @@ -259,26 +259,26 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "" -#: cura/CuraApplication.py:1818 +#: cura/CuraApplication.py:1819 #: cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "" -#: cura/CuraApplication.py:1830 +#: cura/CuraApplication.py:1831 #: cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 @@ -406,17 +406,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "" @@ -3321,17 +3321,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "" @@ -3570,227 +3570,237 @@ msgctxt "@button" msgid "Cancel" msgstr "" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "" -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "" -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "" -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "" -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "" -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "" -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "" -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "" -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "" -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "" -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "" -#: resources/qml/Actions.qml:469 +#: resources/qml/Actions.qml:485 #: resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." @@ -4414,18 +4424,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "" msgstr[1] "" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "" msgstr[1] "" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" @@ -6244,6 +6259,106 @@ msgstr "" msgctxt "@label" msgid "No items to select from" msgstr "" +#: plugins/UltimakerMachineActions/plugin.json +msgctxt "description" +msgid "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.)." +msgstr "" + +#: plugins/UltimakerMachineActions/plugin.json +msgctxt "name" +msgid "UltiMaker machine actions" +msgstr "" + +#: plugins/SupportEraser/plugin.json +msgctxt "description" +msgid "Creates an eraser mesh to block the printing of support in certain places" +msgstr "" + +#: plugins/SupportEraser/plugin.json +msgctxt "name" +msgid "Support Eraser" +msgstr "" + +#: plugins/GCodeGzReader/plugin.json +msgctxt "description" +msgid "Reads g-code from a compressed archive." +msgstr "" + +#: plugins/GCodeGzReader/plugin.json +msgctxt "name" +msgid "Compressed G-code Reader" +msgstr "" + +#: plugins/SentryLogger/plugin.json +msgctxt "description" +msgid "Logs certain events so that they can be used by the crash reporter" +msgstr "" + +#: plugins/SentryLogger/plugin.json +msgctxt "name" +msgid "Sentry Logger" +msgstr "" + +#: plugins/CuraProfileReader/plugin.json +msgctxt "description" +msgid "Provides support for importing Cura profiles." +msgstr "" + +#: plugins/CuraProfileReader/plugin.json +msgctxt "name" +msgid "Cura Profile Reader" +msgstr "" + +#: plugins/3MFWriter/plugin.json +msgctxt "description" +msgid "Provides support for writing 3MF files." +msgstr "" + +#: plugins/3MFWriter/plugin.json +msgctxt "name" +msgid "3MF Writer" +msgstr "" + +#: plugins/PerObjectSettingsTool/plugin.json +msgctxt "description" +msgid "Provides the Per Model Settings." +msgstr "" + +#: plugins/PerObjectSettingsTool/plugin.json +msgctxt "name" +msgid "Per Model Settings Tool" +msgstr "" + +#: plugins/PrepareStage/plugin.json +msgctxt "description" +msgid "Provides a prepare stage in Cura." +msgstr "" + +#: plugins/PrepareStage/plugin.json +msgctxt "name" +msgid "Prepare Stage" +msgstr "" + +#: plugins/TrimeshReader/plugin.json +msgctxt "description" +msgid "Provides support for reading model files." +msgstr "" + +#: plugins/TrimeshReader/plugin.json +msgctxt "name" +msgid "Trimesh Reader" +msgstr "" + +#: plugins/Marketplace/plugin.json +msgctxt "description" +msgid "Manages extensions to the application and allows browsing extensions from the UltiMaker website." +msgstr "" + +#: plugins/Marketplace/plugin.json +msgctxt "name" +msgid "Marketplace" +msgstr "" + #: plugins/LegacyProfileReader/plugin.json msgctxt "description" msgid "Provides support for importing profiles from legacy Cura versions." @@ -6254,34 +6369,294 @@ msgctxt "name" msgid "Legacy Cura Profile Reader" msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json +#: plugins/GCodeProfileReader/plugin.json msgctxt "description" -msgid "Upgrades configurations from Cura 2.2 to Cura 2.4." +msgid "Provides support for importing profiles from g-code files." msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json +#: plugins/GCodeProfileReader/plugin.json msgctxt "name" -msgid "Version Upgrade 2.2 to 2.4" +msgid "G-code Profile Reader" msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json +#: plugins/CuraDrive/plugin.json msgctxt "description" -msgid "Upgrades configurations from Cura 4.11 to Cura 4.12." +msgid "Backup and restore your configuration." msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json +#: plugins/CuraDrive/plugin.json msgctxt "name" -msgid "Version Upgrade 4.11 to 4.12" +msgid "Cura Backups" msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json +#: plugins/3MFReader/plugin.json msgctxt "description" -msgid "Upgrades configurations from Cura 4.1 to Cura 4.2." +msgid "Provides support for reading 3MF files." msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json +#: plugins/3MFReader/plugin.json msgctxt "name" -msgid "Version Upgrade 4.1 to 4.2" +msgid "3MF Reader" +msgstr "" + +#: plugins/X3DReader/plugin.json +msgctxt "description" +msgid "Provides support for reading X3D files." +msgstr "" + +#: plugins/X3DReader/plugin.json +msgctxt "name" +msgid "X3D Reader" +msgstr "" + +#: plugins/RemovableDriveOutputDevice/plugin.json +msgctxt "description" +msgid "Provides removable drive hotplugging and writing support." +msgstr "" + +#: plugins/RemovableDriveOutputDevice/plugin.json +msgctxt "name" +msgid "Removable Drive Output Device Plugin" +msgstr "" + +#: plugins/FirmwareUpdateChecker/plugin.json +msgctxt "description" +msgid "Checks for firmware updates." +msgstr "" + +#: plugins/FirmwareUpdateChecker/plugin.json +msgctxt "name" +msgid "Firmware Update Checker" +msgstr "" + +#: plugins/MachineSettingsAction/plugin.json +msgctxt "description" +msgid "Provides a way to change machine settings (such as build volume, nozzle size, etc.)." +msgstr "" + +#: plugins/MachineSettingsAction/plugin.json +msgctxt "name" +msgid "Machine Settings Action" +msgstr "" + +#: plugins/XmlMaterialProfile/plugin.json +msgctxt "description" +msgid "Provides capabilities to read and write XML-based material profiles." +msgstr "" + +#: plugins/XmlMaterialProfile/plugin.json +msgctxt "name" +msgid "Material Profiles" +msgstr "" + +#: plugins/UFPReader/plugin.json +msgctxt "description" +msgid "Provides support for reading Ultimaker Format Packages." +msgstr "" + +#: plugins/UFPReader/plugin.json +msgctxt "name" +msgid "UFP Reader" +msgstr "" + +#: plugins/CuraEngineBackend/plugin.json +msgctxt "description" +msgid "Provides the link to the CuraEngine slicing backend." +msgstr "" + +#: plugins/CuraEngineBackend/plugin.json +msgctxt "name" +msgid "CuraEngine Backend" +msgstr "" + +#: plugins/GCodeReader/plugin.json +msgctxt "description" +msgid "Allows loading and displaying G-code files." +msgstr "" + +#: plugins/GCodeReader/plugin.json +msgctxt "name" +msgid "G-code Reader" +msgstr "" + +#: plugins/CuraProfileWriter/plugin.json +msgctxt "description" +msgid "Provides support for exporting Cura profiles." +msgstr "" + +#: plugins/CuraProfileWriter/plugin.json +msgctxt "name" +msgid "Cura Profile Writer" +msgstr "" + +#: plugins/SimulationView/plugin.json +msgctxt "description" +msgid "Provides the preview of sliced layerdata." +msgstr "" + +#: plugins/SimulationView/plugin.json +msgctxt "name" +msgid "Simulation View" +msgstr "" + +#: plugins/UM3NetworkPrinting/plugin.json +msgctxt "description" +msgid "Manages network connections to UltiMaker networked printers." +msgstr "" + +#: plugins/UM3NetworkPrinting/plugin.json +msgctxt "name" +msgid "UltiMaker Network Connection" +msgstr "" + +#: plugins/XRayView/plugin.json +msgctxt "description" +msgid "Provides the X-Ray view." +msgstr "" + +#: plugins/XRayView/plugin.json +msgctxt "name" +msgid "X-Ray View" +msgstr "" + +#: plugins/UFPWriter/plugin.json +msgctxt "description" +msgid "Provides support for writing Ultimaker Format Packages." +msgstr "" + +#: plugins/UFPWriter/plugin.json +msgctxt "name" +msgid "UFP Writer" +msgstr "" + +#: plugins/GCodeWriter/plugin.json +msgctxt "description" +msgid "Writes g-code to a file." +msgstr "" + +#: plugins/GCodeWriter/plugin.json +msgctxt "name" +msgid "G-code Writer" +msgstr "" + +#: plugins/MonitorStage/plugin.json +msgctxt "description" +msgid "Provides a monitor stage in Cura." +msgstr "" + +#: plugins/MonitorStage/plugin.json +msgctxt "name" +msgid "Monitor Stage" +msgstr "" + +#: plugins/FirmwareUpdater/plugin.json +msgctxt "description" +msgid "Provides a machine actions for updating firmware." +msgstr "" + +#: plugins/FirmwareUpdater/plugin.json +msgctxt "name" +msgid "Firmware Updater" +msgstr "" + +#: plugins/SolidView/plugin.json +msgctxt "description" +msgid "Provides a normal solid mesh view." +msgstr "" + +#: plugins/SolidView/plugin.json +msgctxt "name" +msgid "Solid View" +msgstr "" + +#: plugins/SliceInfoPlugin/plugin.json +msgctxt "description" +msgid "Submits anonymous slice info. Can be disabled through preferences." +msgstr "" + +#: plugins/SliceInfoPlugin/plugin.json +msgctxt "name" +msgid "Slice info" +msgstr "" + +#: plugins/ImageReader/plugin.json +msgctxt "description" +msgid "Enables ability to generate printable geometry from 2D image files." +msgstr "" + +#: plugins/ImageReader/plugin.json +msgctxt "name" +msgid "Image Reader" +msgstr "" + +#: plugins/PostProcessingPlugin/plugin.json +msgctxt "description" +msgid "Extension that allows for user created scripts for post processing" +msgstr "" + +#: plugins/PostProcessingPlugin/plugin.json +msgctxt "name" +msgid "Post Processing" +msgstr "" + +#: plugins/AMFReader/plugin.json +msgctxt "description" +msgid "Provides support for reading AMF files." +msgstr "" + +#: plugins/AMFReader/plugin.json +msgctxt "name" +msgid "AMF Reader" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade53to54/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 5.3 to Cura 5.4." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade53to54/plugin.json +msgctxt "name" +msgid "Version Upgrade 5.3 to 5.4" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 4.6.0 to Cura 4.6.2." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json +msgctxt "name" +msgid "Version Upgrade 4.6.0 to 4.6.2" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 4.7 to Cura 4.8." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json +msgctxt "name" +msgid "Version Upgrade 4.7 to 4.8" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 4.2 to Cura 4.3." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json +msgctxt "name" +msgid "Version Upgrade 4.2 to 4.3" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 3.2 to Cura 3.3." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json +msgctxt "name" +msgid "Version Upgrade 3.2 to 3.3" msgstr "" #: plugins/VersionUpgrade/VersionUpgrade35to40/plugin.json @@ -6304,136 +6679,6 @@ msgctxt "name" msgid "Version Upgrade 2.7 to 3.0" msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.4 to Cura 4.5." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.4 to 4.5" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 3.0 to Cura 3.1." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json -msgctxt "name" -msgid "Version Upgrade 3.0 to 3.1" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 2.1 to Cura 2.2." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json -msgctxt "name" -msgid "Version Upgrade 2.1 to 2.2" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.13 to Cura 5.0." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.13 to 5.0" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade53to54/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 5.3 to Cura 5.4." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade53to54/plugin.json -msgctxt "name" -msgid "Version Upgrade 5.3 to 5.4" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 3.2 to Cura 3.3." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade32to33/plugin.json -msgctxt "name" -msgid "Version Upgrade 3.2 to 3.3" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.0 to Cura 4.1." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.0 to 4.1" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.2 to Cura 4.3." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade42to43/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.2 to 4.3" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.7 to Cura 4.8." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade47to48/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.7 to 4.8" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 3.4 to Cura 3.5." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json -msgctxt "name" -msgid "Version Upgrade 3.4 to 3.5" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.8 to Cura 4.9." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.8 to 4.9" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.3 to Cura 4.4." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.3 to 4.4" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 2.5 to Cura 2.6." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json -msgctxt "name" -msgid "Version Upgrade 2.5 to 2.6" -msgstr "" - #: plugins/VersionUpgrade/VersionUpgrade462to47/plugin.json msgctxt "description" msgid "Upgrades configurations from Cura 4.6.2 to Cura 4.7." @@ -6444,34 +6689,14 @@ msgctxt "name" msgid "Version Upgrade 4.6.2 to 4.7" msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json msgctxt "description" -msgid "Upgrades configurations from Cura 2.6 to Cura 2.7." +msgid "Upgrades configurations from Cura 2.2 to Cura 2.4." msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade22to24/plugin.json msgctxt "name" -msgid "Version Upgrade 2.6 to 2.7" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.5 to Cura 4.6." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.5 to 4.6" -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 3.3 to Cura 3.4." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json -msgctxt "name" -msgid "Version Upgrade 3.3 to 3.4" +msgid "Version Upgrade 2.2 to 2.4" msgstr "" #: plugins/VersionUpgrade/VersionUpgrade52to53/plugin.json @@ -6484,16 +6709,6 @@ msgctxt "name" msgid "Version Upgrade 5.2 to 5.3" msgstr "" -#: plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json -msgctxt "description" -msgid "Upgrades configurations from Cura 4.6.0 to Cura 4.6.2." -msgstr "" - -#: plugins/VersionUpgrade/VersionUpgrade460to462/plugin.json -msgctxt "name" -msgid "Version Upgrade 4.6.0 to 4.6.2" -msgstr "" - #: plugins/VersionUpgrade/VersionUpgrade49to410/plugin.json msgctxt "description" msgid "Upgrades configurations from Cura 4.9 to Cura 4.10." @@ -6504,114 +6719,144 @@ msgctxt "name" msgid "Version Upgrade 4.9 to 4.10" msgstr "" -#: plugins/CuraProfileWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json msgctxt "description" -msgid "Provides support for exporting Cura profiles." +msgid "Upgrades configurations from Cura 4.8 to Cura 4.9." msgstr "" -#: plugins/CuraProfileWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade48to49/plugin.json msgctxt "name" -msgid "Cura Profile Writer" +msgid "Version Upgrade 4.8 to 4.9" msgstr "" -#: plugins/FirmwareUpdateChecker/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json msgctxt "description" -msgid "Checks for firmware updates." +msgid "Upgrades configurations from Cura 3.0 to Cura 3.1." msgstr "" -#: plugins/FirmwareUpdateChecker/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade30to31/plugin.json msgctxt "name" -msgid "Firmware Update Checker" +msgid "Version Upgrade 3.0 to 3.1" msgstr "" -#: plugins/PostProcessingPlugin/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json msgctxt "description" -msgid "Extension that allows for user created scripts for post processing" +msgid "Upgrades configurations from Cura 3.4 to Cura 3.5." msgstr "" -#: plugins/PostProcessingPlugin/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade34to35/plugin.json msgctxt "name" -msgid "Post Processing" +msgid "Version Upgrade 3.4 to 3.5" msgstr "" -#: plugins/USBPrinting/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json msgctxt "description" -msgid "Accepts G-Code and sends them to a printer. Plugin can also update firmware." +msgid "Upgrades configurations from Cura 4.3 to Cura 4.4." msgstr "" -#: plugins/USBPrinting/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade43to44/plugin.json msgctxt "name" -msgid "USB printing" +msgid "Version Upgrade 4.3 to 4.4" msgstr "" -#: plugins/CuraDrive/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json msgctxt "description" -msgid "Backup and restore your configuration." +msgid "Upgrades configurations from Cura 4.1 to Cura 4.2." msgstr "" -#: plugins/CuraDrive/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade41to42/plugin.json msgctxt "name" -msgid "Cura Backups" +msgid "Version Upgrade 4.1 to 4.2" msgstr "" -#: plugins/GCodeGzWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json msgctxt "description" -msgid "Writes g-code to a compressed archive." +msgid "Upgrades configurations from Cura 4.4 to Cura 4.5." msgstr "" -#: plugins/GCodeGzWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade44to45/plugin.json msgctxt "name" -msgid "Compressed G-code Writer" +msgid "Version Upgrade 4.4 to 4.5" msgstr "" -#: plugins/GCodeWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json msgctxt "description" -msgid "Writes g-code to a file." +msgid "Upgrades configurations from Cura 3.3 to Cura 3.4." msgstr "" -#: plugins/GCodeWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade33to34/plugin.json msgctxt "name" -msgid "G-code Writer" +msgid "Version Upgrade 3.3 to 3.4" msgstr "" -#: plugins/3MFWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json msgctxt "description" -msgid "Provides support for writing 3MF files." +msgid "Upgrades configurations from Cura 4.11 to Cura 4.12." msgstr "" -#: plugins/3MFWriter/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade411to412/plugin.json msgctxt "name" -msgid "3MF Writer" +msgid "Version Upgrade 4.11 to 4.12" msgstr "" -#: plugins/ModelChecker/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json msgctxt "description" -msgid "Checks models and print configuration for possible printing issues and give suggestions." +msgid "Upgrades configurations from Cura 4.13 to Cura 5.0." msgstr "" -#: plugins/ModelChecker/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade413to50/plugin.json msgctxt "name" -msgid "Model Checker" +msgid "Version Upgrade 4.13 to 5.0" msgstr "" -#: plugins/GCodeReader/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json msgctxt "description" -msgid "Allows loading and displaying G-code files." +msgid "Upgrades configurations from Cura 4.5 to Cura 4.6." msgstr "" -#: plugins/GCodeReader/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade45to46/plugin.json msgctxt "name" -msgid "G-code Reader" +msgid "Version Upgrade 4.5 to 4.6" msgstr "" -#: plugins/GCodeGzReader/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json msgctxt "description" -msgid "Reads g-code from a compressed archive." +msgid "Upgrades configurations from Cura 2.1 to Cura 2.2." msgstr "" -#: plugins/GCodeGzReader/plugin.json +#: plugins/VersionUpgrade/VersionUpgrade21to22/plugin.json msgctxt "name" -msgid "Compressed G-code Reader" +msgid "Version Upgrade 2.1 to 2.2" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 4.0 to Cura 4.1." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade40to41/plugin.json +msgctxt "name" +msgid "Version Upgrade 4.0 to 4.1" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 2.5 to Cura 2.6." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade25to26/plugin.json +msgctxt "name" +msgid "Version Upgrade 2.5 to 2.6" +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +msgctxt "description" +msgid "Upgrades configurations from Cura 2.6 to Cura 2.7." +msgstr "" + +#: plugins/VersionUpgrade/VersionUpgrade26to27/plugin.json +msgctxt "name" +msgid "Version Upgrade 2.6 to 2.7" msgstr "" #: plugins/DigitalLibrary/plugin.json @@ -6624,234 +6869,34 @@ msgctxt "name" msgid "Ultimaker Digital Library" msgstr "" -#: plugins/SupportEraser/plugin.json +#: plugins/ModelChecker/plugin.json msgctxt "description" -msgid "Creates an eraser mesh to block the printing of support in certain places" +msgid "Checks models and print configuration for possible printing issues and give suggestions." msgstr "" -#: plugins/SupportEraser/plugin.json +#: plugins/ModelChecker/plugin.json msgctxt "name" -msgid "Support Eraser" +msgid "Model Checker" msgstr "" -#: plugins/RemovableDriveOutputDevice/plugin.json +#: plugins/USBPrinting/plugin.json msgctxt "description" -msgid "Provides removable drive hotplugging and writing support." +msgid "Accepts G-Code and sends them to a printer. Plugin can also update firmware." msgstr "" -#: plugins/RemovableDriveOutputDevice/plugin.json +#: plugins/USBPrinting/plugin.json msgctxt "name" -msgid "Removable Drive Output Device Plugin" +msgid "USB printing" msgstr "" -#: plugins/SolidView/plugin.json +#: plugins/GCodeGzWriter/plugin.json msgctxt "description" -msgid "Provides a normal solid mesh view." +msgid "Writes g-code to a compressed archive." msgstr "" -#: plugins/SolidView/plugin.json +#: plugins/GCodeGzWriter/plugin.json msgctxt "name" -msgid "Solid View" -msgstr "" - -#: plugins/SentryLogger/plugin.json -msgctxt "description" -msgid "Logs certain events so that they can be used by the crash reporter" -msgstr "" - -#: plugins/SentryLogger/plugin.json -msgctxt "name" -msgid "Sentry Logger" -msgstr "" - -#: plugins/PerObjectSettingsTool/plugin.json -msgctxt "description" -msgid "Provides the Per Model Settings." -msgstr "" - -#: plugins/PerObjectSettingsTool/plugin.json -msgctxt "name" -msgid "Per Model Settings Tool" -msgstr "" - -#: plugins/3MFReader/plugin.json -msgctxt "description" -msgid "Provides support for reading 3MF files." -msgstr "" - -#: plugins/3MFReader/plugin.json -msgctxt "name" -msgid "3MF Reader" -msgstr "" - -#: plugins/AMFReader/plugin.json -msgctxt "description" -msgid "Provides support for reading AMF files." -msgstr "" - -#: plugins/AMFReader/plugin.json -msgctxt "name" -msgid "AMF Reader" -msgstr "" - -#: plugins/X3DReader/plugin.json -msgctxt "description" -msgid "Provides support for reading X3D files." -msgstr "" - -#: plugins/X3DReader/plugin.json -msgctxt "name" -msgid "X3D Reader" -msgstr "" - -#: plugins/SimulationView/plugin.json -msgctxt "description" -msgid "Provides the preview of sliced layerdata." -msgstr "" - -#: plugins/SimulationView/plugin.json -msgctxt "name" -msgid "Simulation View" -msgstr "" - -#: plugins/TrimeshReader/plugin.json -msgctxt "description" -msgid "Provides support for reading model files." -msgstr "" - -#: plugins/TrimeshReader/plugin.json -msgctxt "name" -msgid "Trimesh Reader" -msgstr "" - -#: plugins/CuraEngineBackend/plugin.json -msgctxt "description" -msgid "Provides the link to the CuraEngine slicing backend." -msgstr "" - -#: plugins/CuraEngineBackend/plugin.json -msgctxt "name" -msgid "CuraEngine Backend" -msgstr "" - -#: plugins/ImageReader/plugin.json -msgctxt "description" -msgid "Enables ability to generate printable geometry from 2D image files." -msgstr "" - -#: plugins/ImageReader/plugin.json -msgctxt "name" -msgid "Image Reader" -msgstr "" - -#: plugins/GCodeProfileReader/plugin.json -msgctxt "description" -msgid "Provides support for importing profiles from g-code files." -msgstr "" - -#: plugins/GCodeProfileReader/plugin.json -msgctxt "name" -msgid "G-code Profile Reader" -msgstr "" - -#: plugins/XmlMaterialProfile/plugin.json -msgctxt "description" -msgid "Provides capabilities to read and write XML-based material profiles." -msgstr "" - -#: plugins/XmlMaterialProfile/plugin.json -msgctxt "name" -msgid "Material Profiles" -msgstr "" - -#: plugins/MonitorStage/plugin.json -msgctxt "description" -msgid "Provides a monitor stage in Cura." -msgstr "" - -#: plugins/MonitorStage/plugin.json -msgctxt "name" -msgid "Monitor Stage" -msgstr "" - -#: plugins/CuraProfileReader/plugin.json -msgctxt "description" -msgid "Provides support for importing Cura profiles." -msgstr "" - -#: plugins/CuraProfileReader/plugin.json -msgctxt "name" -msgid "Cura Profile Reader" -msgstr "" - -#: plugins/UM3NetworkPrinting/plugin.json -msgctxt "description" -msgid "Manages network connections to UltiMaker networked printers." -msgstr "" - -#: plugins/UM3NetworkPrinting/plugin.json -msgctxt "name" -msgid "UltiMaker Network Connection" -msgstr "" - -#: plugins/PrepareStage/plugin.json -msgctxt "description" -msgid "Provides a prepare stage in Cura." -msgstr "" - -#: plugins/PrepareStage/plugin.json -msgctxt "name" -msgid "Prepare Stage" -msgstr "" - -#: plugins/UFPWriter/plugin.json -msgctxt "description" -msgid "Provides support for writing Ultimaker Format Packages." -msgstr "" - -#: plugins/UFPWriter/plugin.json -msgctxt "name" -msgid "UFP Writer" -msgstr "" - -#: plugins/UltimakerMachineActions/plugin.json -msgctxt "description" -msgid "Provides machine actions for Ultimaker machines (such as bed leveling wizard, selecting upgrades, etc.)." -msgstr "" - -#: plugins/UltimakerMachineActions/plugin.json -msgctxt "name" -msgid "UltiMaker machine actions" -msgstr "" - -#: plugins/UFPReader/plugin.json -msgctxt "description" -msgid "Provides support for reading Ultimaker Format Packages." -msgstr "" - -#: plugins/UFPReader/plugin.json -msgctxt "name" -msgid "UFP Reader" -msgstr "" - -#: plugins/XRayView/plugin.json -msgctxt "description" -msgid "Provides the X-Ray view." -msgstr "" - -#: plugins/XRayView/plugin.json -msgctxt "name" -msgid "X-Ray View" -msgstr "" - -#: plugins/MachineSettingsAction/plugin.json -msgctxt "description" -msgid "Provides a way to change machine settings (such as build volume, nozzle size, etc.)." -msgstr "" - -#: plugins/MachineSettingsAction/plugin.json -msgctxt "name" -msgid "Machine Settings Action" +msgid "Compressed G-code Writer" msgstr "" #: plugins/PreviewStage/plugin.json @@ -6864,33 +6909,3 @@ msgctxt "name" msgid "Preview Stage" msgstr "" -#: plugins/SliceInfoPlugin/plugin.json -msgctxt "description" -msgid "Submits anonymous slice info. Can be disabled through preferences." -msgstr "" - -#: plugins/SliceInfoPlugin/plugin.json -msgctxt "name" -msgid "Slice info" -msgstr "" - -#: plugins/Marketplace/plugin.json -msgctxt "description" -msgid "Manages extensions to the application and allows browsing extensions from the UltiMaker website." -msgstr "" - -#: plugins/Marketplace/plugin.json -msgctxt "name" -msgid "Marketplace" -msgstr "" - -#: plugins/FirmwareUpdater/plugin.json -msgctxt "description" -msgid "Provides a machine actions for updating firmware." -msgstr "" - -#: plugins/FirmwareUpdater/plugin.json -msgctxt "name" -msgid "Firmware Updater" -msgstr "" - diff --git a/resources/i18n/de_DE/cura.po b/resources/i18n/de_DE/cura.po index 32d5befcda..c326cca9fd 100644 --- a/resources/i18n/de_DE/cura.po +++ b/resources/i18n/de_DE/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Login fehlgeschlagen" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Neue Position für Objekte finden" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Position finden" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Innerhalb der Druckabmessung für alle Objekte konnte keine Position gefunden werden" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Kann Position nicht finden" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Es kann nur jeweils ein G-Code gleichzeitig geladen werden. Wichtige {0} werden übersprungen." -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Warnhinweis" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Wenn G-Code geladen wird, kann keine weitere Datei geöffnet werden. Wichtige {0} werden übersprungen." -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Berechnet" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Objekte vervielfältigen und platzieren" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Objekte platzieren" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Objekt-Platzierung" @@ -3675,17 +3675,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Drucker entfernen" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Drucken über Netzwerk" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Drücken über Netzwerk" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Über Netzwerk verbunden" @@ -4227,227 +4227,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Abbrechen" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Online-Fehlerbehebung anzeigen" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Umschalten auf Vollbild-Modus" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Vollbildmodus beenden" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Rückgängig machen" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Wiederholen" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Beenden" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3D-Ansicht" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Vorderansicht" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Draufsicht" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Ansicht von unten" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Ansicht von links" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Ansicht von rechts" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Cura konfigurieren..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Drucker hinzufügen..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Dr&ucker verwalten..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Materialien werden verwaltet..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Weiteres Material aus Marketplace hinzufügen" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Profil mit aktuellen Einstellungen/Überschreibungen aktualisieren" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Aktuelle Änderungen verwerfen" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "P&rofil von aktuellen Einstellungen/Überschreibungen erstellen..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Profile verwalten..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Online-&Dokumentation anzeigen" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "&Fehler melden" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Neuheiten" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Über..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Ausgewählte löschen" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Ausgewählte zentrieren" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Ausgewählte vervielfachen" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Modell löschen" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Modell auf Druckplatte ze&ntrieren" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "Modelle &gruppieren" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Gruppierung für Modelle aufheben" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "Modelle &zusammenführen" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "Modell &multiplizieren..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Alle Modelle wählen" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Druckplatte reinigen" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Alle Modelle neu laden" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Alle Modelle anordnen" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Anordnung auswählen" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Alle Modellpositionen zurücksetzen" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Alle Modelltransformationen zurücksetzen" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Datei(en) öffnen..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Neues Projekt..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Konfigurationsordner anzeigen" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Sichtbarkeit einstellen wird konfiguriert..." @@ -5071,18 +5081,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Ausgewähltes Modell drucken mit:" msgstr[1] "Ausgewählte Modelle drucken mit:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Ausgewähltes Modell multiplizieren" msgstr[1] "Ausgewählte Modelle multiplizieren" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Anzahl Kopien" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/es_ES/cura.po b/resources/i18n/es_ES/cura.po index 991ce49f1e..f8f3eddb49 100644 --- a/resources/i18n/es_ES/cura.po +++ b/resources/i18n/es_ES/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -22,22 +22,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Fallo de inicio de sesión" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Buscando nueva ubicación para los objetos" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Buscando ubicación" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "No se puede encontrar una ubicación dentro del volumen de impresión para todos los objetos" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "No se puede encontrar la ubicación" @@ -264,25 +264,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Solo se puede cargar un archivo GCode a la vez. Se omitió la importación de {0}" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Advertencia" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "No se puede abrir ningún archivo si se está cargando un archivo GCode. Se omitió la importación de {0}" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -409,17 +409,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Calculado" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Multiplicar y colocar objetos" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Colocando objetos" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Colocando objeto" @@ -3675,17 +3675,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Eliminar impresoras" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Imprimir a través de la red" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Imprime a través de la red" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Conectado a través de la red" @@ -4227,227 +4227,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Cancelar" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Mostrar resolución de problemas online" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Alternar pantalla completa" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Salir de modo de pantalla completa" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "Des&hacer" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Rehacer" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Salir" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "Vista en 3D" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Vista frontal" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Vista superior" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Vista inferior" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Vista del lado izquierdo" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Vista del lado derecho" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Configurar Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Agregar impresora..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Adm&inistrar impresoras ..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Administrar materiales..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Añadir más materiales de Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Actualizar perfil con ajustes o sobrescrituras actuales" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Descartar cambios actuales" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Crear perfil a partir de ajustes o sobrescrituras actuales..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Administrar perfiles..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Mostrar &documentación en línea" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Informar de un &error" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Novedades" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Acerca de..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Eliminar selección" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Centrar selección" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Multiplicar selección" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Eliminar modelo" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Ce&ntrar modelo en plataforma" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "A&grupar modelos" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Desagrupar modelos" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "Co&mbinar modelos" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Multiplicar modelo..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Seleccionar todos los modelos" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Borrar placa de impresión" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Recargar todos los modelos" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Organizar todos los modelos" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Organizar selección" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Restablecer las posiciones de todos los modelos" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Restablecer las transformaciones de todos los modelos" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Abrir archivo(s)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Nuevo proyecto..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Mostrar carpeta de configuración" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Configurar visibilidad de los ajustes..." @@ -5071,18 +5081,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Imprimir modelo seleccionado con:" msgstr[1] "Imprimir modelos seleccionados con:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Multiplicar modelo seleccionado" msgstr[1] "Multiplicar modelos seleccionados" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Número de copias" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/fi_FI/cura.po b/resources/i18n/fi_FI/cura.po index f2b4e4cbcb..95c45c0ea1 100644 --- a/resources/i18n/fi_FI/cura.po +++ b/resources/i18n/fi_FI/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: 2022-07-15 10:53+0200\n" "Last-Translator: Bothof \n" "Language-Team: Finnish\n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Uusien paikkojen etsiminen kappaleille" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Etsitään paikkaa" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Kaikille kappaleille ei löydy paikkaa tulostustilavuudessa" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Paikkaa ei löydy" @@ -257,25 +257,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Vain yksi G-code-tiedosto voidaan ladata kerralla. Tiedoston {0} tuonti ohitettiin." -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Varoitus" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Muita tiedostoja ei voida ladata, kun G-code latautuu. Tiedoston {0} tuonti ohitettiin." -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -402,17 +402,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Laskettu" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Kappaleiden kertominen ja sijoittelu" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Sijoitetaan kappaletta" @@ -3650,17 +3650,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Tulosta verkon kautta" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Tulosta verkon kautta" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "" @@ -4199,227 +4199,237 @@ msgctxt "@button" msgid "Cancel" msgstr "" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Vaihda koko näyttöön" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Kumoa" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "Tee &uudelleen" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Lopeta" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Määritä Curan asetukset..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "L&isää tulostin..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Tulostinten &hallinta..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Hallitse materiaaleja..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Päivitä nykyiset asetukset tai ohitukset profiiliin" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Hylkää tehdyt muutokset" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Luo profiili nykyisten asetusten tai ohitusten perusteella..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Profiilien hallinta..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Näytä sähköinen &dokumentaatio" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Ilmoita &virheestä" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Tietoja..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Poista malli" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Ke&skitä malli alustalle" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "&Ryhmittele mallit" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Poista mallien ryhmitys" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "&Yhdistä mallit" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Kerro malli..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Valitse kaikki mallit" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Tyhjennä tulostusalusta" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Lataa kaikki mallit uudelleen" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Järjestä kaikki mallit" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Järjestä valinta" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Määritä kaikkien mallien positiot uudelleen" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Määritä kaikkien mallien muutokset uudelleen" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Avaa tiedosto(t)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Uusi projekti..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Näytä määrityskansio" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Määritä asetusten näkyvyys..." @@ -5040,18 +5050,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Tulosta valittu malli asetuksella:" msgstr[1] "Tulosta valitut mallit asetuksella:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Kerro valittu malli" msgstr[1] "Kerro valitut mallit" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Kopioiden määrä" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/fr_FR/cura.po b/resources/i18n/fr_FR/cura.po index ea1d51cd5a..e496faed64 100644 --- a/resources/i18n/fr_FR/cura.po +++ b/resources/i18n/fr_FR/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "La connexion a échoué" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Recherche d'un nouvel emplacement pour les objets" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Recherche d'emplacement" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Impossible de trouver un emplacement dans le volume d'impression pour tous les objets" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Impossible de trouver un emplacement" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Un seul fichier G-Code peut être chargé à la fois. Importation de {0} sautée" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Avertissement" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Impossible d'ouvrir un autre fichier si le G-Code est en cours de chargement. Importation de {0} sautée" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Calculer" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Multiplication et placement d'objets" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Placement des objets" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Placement de l'objet" @@ -3678,17 +3678,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Supprimer des imprimantes" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Imprimer sur le réseau" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Imprimer sur le réseau" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Connecté sur le réseau" @@ -4227,227 +4227,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Annuler" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Afficher le dépannage en ligne" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Passer en Plein écran" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Quitter le mode plein écran" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Annuler" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Rétablir" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Quitter" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "Vue 3D" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Vue de face" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Vue du dessus" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Vue de dessous" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Vue latérale gauche" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Vue latérale droite" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Configurer Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Ajouter une imprimante..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Gérer les &imprimantes..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Gérer les matériaux..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Ajouter d'autres matériaux depuis la Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Mettre à jour le profil à l'aide des paramètres / forçages actuels" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Ignorer les modifications actuelles" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Créer un profil à partir des paramètres / forçages actuels..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Gérer les profils..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Afficher la &documentation en ligne" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Notifier un &bug" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Quoi de neuf" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "À propos de..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Supprimer la sélection" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Centrer la sélection" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Multiplier la sélection" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Supprimer le modèle" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Ce&ntrer le modèle sur le plateau" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "&Grouper les modèles" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Dégrouper les modèles" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "&Fusionner les modèles" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Multiplier le modèle..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Sélectionner tous les modèles" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Supprimer les modèles du plateau" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Recharger tous les modèles" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Réorganiser tous les modèles" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Réorganiser la sélection" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Réinitialiser toutes les positions des modèles" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Réinitialiser tous les modèles et transformations" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Ouvrir le(s) fichier(s)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Nouveau projet..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Afficher le dossier de configuration" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Configurer la visibilité des paramètres..." @@ -5071,18 +5081,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Imprimer le modèle sélectionné avec :" msgstr[1] "Imprimer les modèles sélectionnés avec :" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Multiplier le modèle sélectionné" msgstr[1] "Multiplier les modèles sélectionnés" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Nombre de copies" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/hu_HU/cura.po b/resources/i18n/hu_HU/cura.po index 9ea5ca5aea..06e99060f7 100644 --- a/resources/i18n/hu_HU/cura.po +++ b/resources/i18n/hu_HU/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: 2020-03-24 09:36+0100\n" "Last-Translator: Nagy Attila \n" "Language-Team: ATI-SZOFT\n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Sikertelen bejelentkezés" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Új hely keresése az objektumokhoz" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Hely keresés" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Nincs elég hely az összes objektum építési térfogatához" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Nem találok helyet" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Egyszerre csak egy G-kód fájlt lehet betölteni. Az importálás kihagyva {0}" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Figyelem" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Nem nyitható meg más fájl, ha a G-kód betöltődik. Az importálás kihagyva {0}" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Számított" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Tárgyak többszörözése és elhelyezése" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Tárgyak elhelyezése" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Tárgy elhelyezése" @@ -3666,17 +3666,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Hálózati nyomtatás" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Hálózati nyomtatás" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Csatlakozva hálózaton keresztül" @@ -4215,227 +4215,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Elvet" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Teljes képernyőre váltás" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Kilépés a teljes képernyőn" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Visszavon" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Újra" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "Kilép" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3D nézet" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Előlnézet" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Felülnézet" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Bal oldalnézet" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Jobb oldalnézet" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Cura beállítása..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Nyomtató hozzáadása..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Nyomtatók kezelése..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Anyagok kezelése..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "Profil &frissítése a jelenlegi beállításokkal/felülírásokkal" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Jelenlegi változtatások eldobása" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "Profil &létrehozása a jelenlegi beállításokkal/felülírásokkal..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Profilok kezelése..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Online &Dokumentumok megjelenítése" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Hibajelentés" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Újdonságok" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Rólunk..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Modell törlés" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "&Középső modell a platformon" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "&Csoportosítás" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Csoport bontása" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "&Modellek keverése" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Modell többszörözés..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Mindent kijelöl" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Tárgyasztal törlése" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Mindent újratölt" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Minden modell rendezése" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Kijelöltek rendezése" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Minden modellpozíció visszaállítása" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Minden modelltranszformáció visszaállítása" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "Fájl(ok) megnyitása..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "Új projekt..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Konfigurációs mappa megjelenítése" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Beállítások láthatóságának beállítása..." @@ -5054,18 +5064,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Kiválasztott modell nyomtatása:" msgstr[1] "Kiválasztott modellek nyomtatása:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Kiválasztott modell sokszorozása" msgstr[1] "Kiválasztott modellek sokszorozása" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Másolatok száma" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/it_IT/cura.po b/resources/i18n/it_IT/cura.po index ea4fc72cff..8151bb48c9 100644 --- a/resources/i18n/it_IT/cura.po +++ b/resources/i18n/it_IT/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Login non riuscito" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Ricerca nuova posizione per gli oggetti" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Ricerca posizione" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Impossibile individuare una posizione nel volume di stampa per tutti gli oggetti" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Impossibile individuare posizione" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "È possibile caricare un solo file codice G per volta. Importazione saltata {0}" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Avvertenza" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Impossibile aprire altri file durante il caricamento del codice G. Importazione saltata {0}" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Calcolato" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Moltiplicazione e collocazione degli oggetti" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Sistemazione oggetti" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Sistemazione oggetto" @@ -3678,17 +3678,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Rimuovere le stampanti" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Stampa sulla rete" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Stampa sulla rete" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Collegato alla rete" @@ -4230,227 +4230,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Annulla" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Mostra ricerca e riparazione dei guasti online" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Attiva/disattiva schermo intero" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Esci da schermo intero" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Annulla" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "Ri&peti" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Esci" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "Visualizzazione 3D" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Visualizzazione frontale" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Visualizzazione superiore" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Vista inferiore" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Visualizzazione lato sinistro" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Visualizzazione lato destro" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Configura Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Aggiungi stampante..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Gestione stampanti..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Gestione materiali..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Aggiungere altri materiali da Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Aggiorna il profilo con le impostazioni/esclusioni correnti" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Elimina le modifiche correnti" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Crea profilo dalle impostazioni/esclusioni correnti..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Gestione profili..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Mostra documentazione &online" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Se&gnala un errore" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Scopri le novità" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Informazioni..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Cancella selezionati" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Centra selezionati" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Moltiplica selezionati" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Elimina modello" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "C&entra modello su piattaforma" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "&Raggruppa modelli" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Separa modelli" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "&Unisci modelli" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "Mo<iplica modello..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Seleziona tutti i modelli" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Cancellare piano di stampa" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Ricarica tutti i modelli" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Sistema tutti i modelli" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Sistema selezione" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Reimposta tutte le posizioni dei modelli" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Reimposta tutte le trasformazioni dei modelli" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Apri file..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Nuovo Progetto..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Mostra cartella di configurazione" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Configura visibilità delle impostazioni..." @@ -5074,18 +5084,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Stampa modello selezionato con:" msgstr[1] "Stampa modelli selezionati con:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Moltiplica modello selezionato" msgstr[1] "Moltiplica modelli selezionati" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Numero di copie" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/ja_JP/cura.po b/resources/i18n/ja_JP/cura.po index 3a896394a5..7609c93e0f 100644 --- a/resources/i18n/ja_JP/cura.po +++ b/resources/i18n/ja_JP/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "ログインに失敗しました" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "造形物のために新しい位置を探索中" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "位置確認" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "全ての造形物の造形サイズに対し、適切な位置が確認できません" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "位置を確保できません" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "一度に一つのG-codeしか読み取れません。{0}の取り込みをスキップしました。" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "警告" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "G-codeを読み込み中は他のファイルを開くことができません。{0}の取り込みをスキップしました。" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "計算された" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "造形データを増やす、配置する" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "造形データを配置" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "造形データを配置" @@ -3666,17 +3666,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "プリンターを取り除く" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "ネットワーク上のプリント" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "ネットワークのプリント" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "ネットワーク上で接続" @@ -4218,227 +4218,237 @@ msgctxt "@button" msgid "Cancel" msgstr "キャンセル" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "オンライントラブルシューティングを表示" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "留め金 フルスクリーン" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "全画面表示を終了する" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&取り消す" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&やりなおす" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&やめる" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3Dビュー" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "フロントビュー" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "トップビュー" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "底面図" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "左サイドビュー" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "右サイドビュー" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Curaを構成する..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&プリンターを追加する..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "プリンターを管理する..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "フィラメントを管理する..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Add more materials from Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&現在の設定/無効にプロファイルをアップデートする" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&変更を破棄する" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&今の設定/無効からプロファイルを作成する..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "プロファイルを管理する..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "オンラインドキュメントを表示する" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "報告&バグ" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "新情報" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "アバウト..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "選択内容を削除" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "選択内容を中央に移動" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "選択内容を増倍" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "モデルを消去する" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "プラットホームの中心にモデルを配置" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "&モデルグループ" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "モデルを非グループ化" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "モ&デルの合体" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&モデルを増倍する..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "すべてのモデル選択" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "ビルドプレート上のクリア" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "すべてのモデルを読み込む" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "すべてのモデルをアレンジする" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "選択をアレンジする" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "すべてのモデルのポジションをリセットする" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "すべてのモデル&変更点をリセットする" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&ファイルを開く(s)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&新しいプロジェクト..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "コンフィグレーションのフォルダーを表示する" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "視野のセッティングを構成する..." @@ -5061,17 +5071,22 @@ msgid "Print Selected Model With:" msgid_plural "Print Selected Models With:" msgstr[0] "選択したモデルで印刷:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "選択した複数のモデル" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "コピーの数" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/ko_KR/cura.po b/resources/i18n/ko_KR/cura.po index f7a5f47e0a..45af01145e 100644 --- a/resources/i18n/ko_KR/cura.po +++ b/resources/i18n/ko_KR/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "로그인 실패" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "객체의 새 위치 찾기" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "위치 찾기" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "모든 개체가 출력할 수 있는 최대 사이즈 내에 위치할 수 없습니다" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "위치를 찾을 수 없음" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "한 번에 하나의 G-코드 파일만 로드 할 수 있습니다. {0} 가져 오기를 건너 뛰었습니다." -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "경고" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "G-코드가 로드되어 있으면 다른 파일을 열 수 없습니다. {0} 가져 오기를 건너 뛰었습니다." -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "계산된" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "객체를 증가시키고 배치" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "개체 배치 중" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "개체 배치 중" @@ -3665,17 +3665,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "프린터 제거" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "네트워크를 통해 프린팅" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "네트워크를 통해 프린팅" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "네트워크를 통해 연결됨" @@ -4217,227 +4217,237 @@ msgctxt "@button" msgid "Cancel" msgstr "취소" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "온라인 문제 해결 표시" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "전채 화면 전환" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "전체 화면 종료" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "되돌리기(&U)" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "다시하기(&R)" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "종료(&Q)" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3D 보기" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "앞에서 보기" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "위에서 보기" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "하단 뷰" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "왼쪽에서 보기" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "오른쪽에서 보기" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Cura 구성 ..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "프린터 추가..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "프린터 관리 ..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "재료 관리..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "마켓플레이스에서 더 많은 재료 추가" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "현재 설정으로로 프로파일 업데이트" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "현재 변경 사항 무시" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "현재 설정으로 프로파일 생성..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "프로파일 관리..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "온라인 문서 표시" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "버그 리포트" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "새로운 기능" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "소개..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "선택 항목 삭제" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "선택 항목 가운데 정렬" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "선택 항목 복제" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "모델 삭제" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "플랫폼중심에 모델 위치하기" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "모델 그룹화" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "모델 그룹 해제" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "모델 합치기" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "모델 복제..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "모든 모델 선택" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "빌드 플레이트 지우기" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "모든 모델 다시 로드" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "모든 모델 정렬" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "선택한 모델 정렬" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "모든 모델의 위치 재설정" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "모든 모델의 변환 재설정" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "파일 열기..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "새로운 프로젝트..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "설정 폴더 표시" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "설정 보기..." @@ -5060,17 +5070,22 @@ msgid "Print Selected Model With:" msgid_plural "Print Selected Models With:" msgstr[0] "선택된 모델 프린팅 :" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "선택한 모델 복" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "복제할 수" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/nl_NL/cura.po b/resources/i18n/nl_NL/cura.po index 6c903dd42c..6a3bb768c4 100644 --- a/resources/i18n/nl_NL/cura.po +++ b/resources/i18n/nl_NL/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Inloggen mislukt" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Nieuwe locatie vinden voor objecten" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Locatie vinden" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Kan binnen het werkvolume niet voor alle objecten een locatie vinden" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Kan locatie niet vinden" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Er kan slechts één G-code-bestand tegelijkertijd worden geladen. Het importeren van {0} is overgeslagen" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Waarschuwing" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Kan geen ander bestand openen als G-code wordt geladen. Het importeren van {0} is overgeslagen" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Berekend" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Objecten verveelvoudigen en plaatsen" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Objecten plaatsen" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Object plaatsen" @@ -3678,17 +3678,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Printers verwijderen" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Printen via netwerk" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Printen via netwerk" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Via het netwerk verbonden" @@ -4230,227 +4230,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Annuleren" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Online probleemoplossing weergeven" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Volledig Scherm In-/Uitschakelen" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Volledig scherm sluiten" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "Ongedaan &Maken" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Opnieuw" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Afsluiten" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3D-weergave" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Weergave voorzijde" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Weergave bovenzijde" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Aanzicht onderzijde" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Weergave linkerzijde" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Weergave rechterzijde" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Cura Configureren..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Printer Toevoegen..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Pr&inters Beheren..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Materialen Beheren..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Meer materialen toevoegen van Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "Profiel bijwerken met h&uidige instellingen/overschrijvingen" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "Hui&dige wijzigingen verwijderen" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "Profiel maken op basis van huidige instellingen/overs&chrijvingen..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Profielen Beheren..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Online &Documentatie Weergeven" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Een &Bug Rapporteren" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Nieuwe functies" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Over..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Verwijder geselecteerde items" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Centreer geselecteerde items" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Verveelvoudig geselecteerde items" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Model Verwijderen" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Model op Platform Ce&ntreren" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "Modellen &Groeperen" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Groeperen van Modellen Opheffen" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "Modellen Samen&voegen" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Model verveelvoudigen..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Alle Modellen Selecteren" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Platform Leegmaken" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Alle Modellen Opnieuw Laden" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Alle modellen schikken" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Selectie schikken" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Alle Modelposities Herstellen" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Alle Modeltransformaties Herstellen" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "Bestand(en) &openen..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Nieuw project..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Open Configuratiemap" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Zichtbaarheid Instelling Configureren..." @@ -5074,18 +5084,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Geselecteerd model printen met:" msgstr[1] "Geselecteerde modellen printen met:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Geselecteerd model verveelvoudigen" msgstr[1] "Geselecteerde modellen verveelvoudigen" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Aantal exemplaren" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/pl_PL/cura.po b/resources/i18n/pl_PL/cura.po index 6bdef88ffd..27734220ad 100644 --- a/resources/i18n/pl_PL/cura.po +++ b/resources/i18n/pl_PL/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: 2021-09-07 08:02+0200\n" "Last-Translator: Mariusz Matłosz \n" "Language-Team: Mariusz Matłosz , reprapy.pl\n" @@ -24,22 +24,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Logowanie nie powiodło się" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Znajdowanie nowej lokalizacji obiektów" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Szukanie Lokalizacji" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Nie można znaleźć lokalizacji w obrębie obszaru roboczego dla wszystkich obiektów" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Nie można Znaleźć Lokalizacji" @@ -266,25 +266,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Jednocześnie można załadować tylko jeden plik G-code. Pominięto importowanie {0}" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Ostrzeżenie" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Nie można otworzyć żadnego innego pliku, jeśli ładuje się G-code. Pominięto importowanie {0}" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -411,17 +411,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Przeliczone" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Zwielokrotnienie i umieszczanie przedmiotów" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Umieść Obiekty" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Rozmieszczenie Obiektów" @@ -3667,17 +3667,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Drukuj przez sieć" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Drukuj przez sieć" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Połączone przez sieć" @@ -4216,227 +4216,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Anuluj" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Przełącz tryb pełnoekranowy" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Wyłącz tryb pełnoekranowy" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Cofnij" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Ponów" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Zamknij" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "Widok 3D" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Widok z przodu" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Widok z góry" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Widok z lewej strony" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Widok z prawej strony" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Konfiguruj Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Dodaj drukarkę..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Zarządzaj drukarkami..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Zarządzaj materiałami..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Aktualizuj profil z bieżącymi ustawieniami" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Odrzuć bieżące zmiany" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Utwórz profil z bieżących ustawień..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Zarządzaj profilami..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Pokaż dokumentację internetową" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Zgłoś błąd" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Co nowego" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "O..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Usuń model" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Wyśrodkuj model na platformie" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "&Grupuj modele" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Rozgrupuj modele" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "Połącz modele" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Powiel model..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Wybierz wszystkie modele" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Wyczyść stół" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Przeładuj wszystkie modele" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Ułóż wszystkie modele" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Wybór ułożenia" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Zresetuj wszystkie pozycje modelu" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Zresetuj wszystkie przekształcenia modelu" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Otwórz plik(i)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Nowy projekt..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Pokaż folder konfiguracji" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Skonfiguruj widoczność ustawień ..." @@ -5057,18 +5067,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Wydrukuj wybrany model z:" msgstr[1] "Wydrukuj wybrane modele z:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Zduplikuj wybrany model" msgstr[1] "Zduplikuj wybrane modele" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Liczba kopii" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/pt_BR/cura.po b/resources/i18n/pt_BR/cura.po index 4242a852d5..1973e9e3ed 100644 --- a/resources/i18n/pt_BR/cura.po +++ b/resources/i18n/pt_BR/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: 2023-02-17 17:37+0100\n" "Last-Translator: Cláudio Sampaio \n" "Language-Team: Cláudio Sampaio \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Login falhou" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Achando novos lugares para objetos" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Buscando Localização" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Não foi possível achar um lugar dentro do volume de construção para todos os objetos" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Não Foi Encontrada Localização" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Somente um arquivo G-Code pode ser carregado por vez. Pulando importação de {0}" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Aviso" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Não é possível abrir nenhum outro arquivo se G-Code estiver sendo carregado. Pulando importação de {0}" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Calculado" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Multiplicando e colocando objetos" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Colocando Objetos" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Colocando Objeto" @@ -3678,17 +3678,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Remover impressoras" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Imprimir pela rede" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Imprime pela rede" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Conectado pela rede" @@ -4230,227 +4230,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Cancelar" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Mostrar Resolução de Problemas Online" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Alternar Tela Cheia" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Sair da Tela Cheia" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "Desfazer (&U)" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Refazer" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "Sair (&Q)" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "Visão &3D" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Visão Frontal" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Visão Superior" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Visão de Baixo" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Visão do Lado Esquerdo" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Visão do Lado Direito" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Configurar Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Adicionar Impressora..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Adm&inistrar Impressoras..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Administrar Materiais..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Adicionar mais materiais ao Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "At&ualizar perfil com valores e sobreposições atuais" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Descartar ajustes atuais" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Criar perfil a partir de ajustes/sobreposições atuais..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Administrar perfis..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Exibir &Documentação Online" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Relatar um &Bug" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Novidades" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Sobre..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Remover Selecionados" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Centralizar Selecionados" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Multiplicar Selecionados" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Remover Modelo" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Ce&ntralizar Modelo na Mesa" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "A&grupar Modelos" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Desagrupar Modelos" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "Co&mbinar Modelos" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Multiplicar Modelo..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Selecionar Todos Os Modelos" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Esvaziar a Mesa de Impressão" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Recarregar Todos Os Modelos" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Posicionar Todos os Modelos" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Posicionar Seleção" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Reestabelecer as Posições de Todos Os Modelos" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Remover as Transformações de Todos Os Modelos" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "Abrir Arquiv&o(s)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Novo Projeto..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Exibir Pasta de Configuração" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Configurar a visibilidade dos ajustes..." @@ -5071,18 +5081,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Imprimir Modelo Selecionado Com:" msgstr[1] "Imprimir Modelos Selecionados Com:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Multiplicar Modelo Selecionado" msgstr[1] "Multiplicar Modelos Selecionados" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Número de Cópias" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/pt_PT/cura.po b/resources/i18n/pt_PT/cura.po index 21eace1a53..2f683461e6 100644 --- a/resources/i18n/pt_PT/cura.po +++ b/resources/i18n/pt_PT/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Falha no início de sessão" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "A procurar nova posição para os objetos" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "A Procurar Posição" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Não é possível posicionar todos os objetos dentro do volume de construção" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Não é Possível Posicionar" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Apenas pode ser carregado um ficheiro G-code de cada vez. Importação {0} ignorada" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Aviso" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Não é possível abrir outro ficheiro enquanto o G-code estiver a carregar. Importação {0} ignorada" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Calculado" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Multiplicar e posicionar objetos" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "A posicionar objetos" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "A Posicionar Objeto" @@ -3676,17 +3676,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Remover impressoras" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Imprimir através da rede" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Imprimir através da rede" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Ligado através da rede" @@ -4228,227 +4228,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Cancelar" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Ver online o guia de resolução de problemas" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Alternar para ecrã inteiro" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Sair do Ecrã Inteiro" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Desfazer" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Refazer" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Sair" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "Vista 3D" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Vista Frente" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Vista Cima" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Vista Inferior" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Vista Lado Esquerdo" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Vista Lado Direito" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Configurar Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Adicionar Impressora..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Gerir Im&pressoras..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Gerir Materiais..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Adicionar mais materiais disponíveis no Marketplace" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Atualizar perfil com as definições/substituições atuais" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Descartar alterações atuais" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "&Criar perfil a partir das definições/substituições atuais..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Gerir Perfis..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Mostrar &documentação online" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Reportar um &erro" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Novidades" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Sobre..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Apagar seleção" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Centrar seleção" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Multiplicar seleção" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Apagar Modelo" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Ce&ntrar Modelo na Base" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "&Agrupar Modelos" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Desagrupar Modelos" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "&Combinar Modelos" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Multiplicar Modelo..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Selecionar todos os modelos" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Limpar base de construção" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Recarregar todos os modelos" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Dispor todos os modelos" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Dispor seleção" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Repor todas as posições de modelos" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Repor Todas as Transformações do Modelo" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Abrir Ficheiro(s)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Novo Projeto..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Mostrar pasta de configuração" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Configurar visibilidade das definições..." @@ -5072,18 +5082,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Imprimir Modelo Selecionado Com:" msgstr[1] "Imprimir modelos selecionados com:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Multiplicar Modelo Selecionado" msgstr[1] "Multiplicar modelos selecionados" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Número de Cópias" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/ru_RU/cura.po b/resources/i18n/ru_RU/cura.po index a43df3e590..15431cabc9 100644 --- a/resources/i18n/ru_RU/cura.po +++ b/resources/i18n/ru_RU/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Вход не выполнен" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Поиск места для новых объектов" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Поиск места" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Невозможно разместить все объекты внутри печатаемого объёма" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Не могу найти место" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f мм" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Только один G-code файла может быть загружен в момент времени. Пропускаю импортирование {0}" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Внимание" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "Невозможно открыть любой другой файл, если G-code файл уже загружен. Пропускаю импортирование {0}" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Вычислено" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Размножение и размещение объектов" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Размещение объектов" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Размещение объекта" @@ -3687,17 +3687,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Удалить принтеры" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Печать через сеть" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Печать через сеть" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Подключен по сети" @@ -4239,227 +4239,237 @@ msgctxt "@button" msgid "Cancel" msgstr "Отмена" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Показать сетевое руководство по устранению неполадок" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Полный экран" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Выйти из полноэкранного режима" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "Отмена" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "Возврат" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "Выход" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "Трехмерный вид" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Вид спереди" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Вид сверху" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Вид снизу" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Вид слева" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Вид справа" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Настроить Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "Добавить принтер..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Управление принтерами..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Управление материалами..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Добавить больше материалов из Магазина" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "Обновить профиль текущими параметрами" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "Сбросить текущие параметры" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "Создать профиль из текущих параметров..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Управление профилями..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Показать онлайн документацию" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Отправить отчёт об ошибке" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Что нового" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "О Cura..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Удалить выбранное" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Центрировать выбранное" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Размножить выбранное" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Удалить модель" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Поместить модель по центру" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "Сгруппировать модели" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Разгруппировать модели" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "Объединить модели" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "Дублировать модель..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Выбрать все модели" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Очистить стол" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Перезагрузить все модели" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Выровнять все модели" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Выровнять выбранные" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Сбросить позиции всех моделей" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Сбросить преобразования всех моделей" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "Открыть файл(ы)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "Новый проект..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Показать конфигурационный каталог" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Видимость параметров..." @@ -5084,7 +5094,7 @@ msgstr[0] "Печать выбранной модели:" msgstr[1] "Печать выбранных моделей:" msgstr[2] "Печать выбранных моделей:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" @@ -5092,11 +5102,16 @@ msgstr[0] "Размножить выбранную модель" msgstr[1] "Размножить выбранные модели" msgstr[2] "Размножить выбранные модели" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Количество копий" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/tr_TR/cura.po b/resources/i18n/tr_TR/cura.po index 7731155049..f607ed1c83 100644 --- a/resources/i18n/tr_TR/cura.po +++ b/resources/i18n/tr_TR/cura.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "Giriş başarısız" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "Nesneler için yeni konum bulunuyor" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "Konumu Buluyor" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "Yapılan hacim içinde tüm nesneler için konum bulunamadı" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "Konum Bulunamıyor" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "Aynı anda yalnızca bir G-code dosyası yüklenebilir. {0} içe aktarma atlandı" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "Uyarı" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "G-code yüklenirken başka bir dosya açılamaz. {0} içe aktarma atlandı" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "Hesaplanmış" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "Nesneler çoğaltılıyor ve yerleştiriliyor" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "Nesneler Yerleştiriliyor" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "Nesne Yerleştiriliyor" @@ -3678,17 +3678,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "Yazıcıları kaldır" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "Ağ üzerinden yazdır" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "Ağ üzerinden yazdır" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "Ağ üzerinden bağlandı" @@ -4230,227 +4230,237 @@ msgctxt "@button" msgid "Cancel" msgstr "İptal" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "Çevrimiçi Sorun Giderme Kılavuzunu Göster" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "Tam Ekrana Geç" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "Tam Ekrandan Çık" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "&Geri Al" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "&Yinele" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "&Çıkış" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3 Boyutlu Görünüm" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "Önden Görünüm" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "Yukarıdan Görünüm" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "Alttan Görünüm" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "Sol Taraftan Görünüm" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "Sağ Taraftan Görünüm" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "Cura’yı yapılandır..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "&Yazıcı Ekle..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "Yazıcıları Yönet..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "Malzemeleri Yönet..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "Mağazadan daha fazla malzeme ekle" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "&Profili geçerli ayarlar/geçersiz kılmalar ile güncelle" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "&Geçerli değişiklikleri iptal et" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "G&eçerli ayarlardan/geçersiz kılmalardan profil oluştur..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "Profilleri Yönet..." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "Çevrimiçi Belgeleri Göster" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "Hata Bildir" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "Yenilikler" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "Hakkında..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "Seçileni Sil" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "Seçileni Ortala" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "Seçileni Çoğalt" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "Modeli Sil" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "Modeli Platformda Ortala" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "Modelleri Gruplandır" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "Model Grubunu Çöz" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "&Modelleri Birleştir" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "&Modeli Çoğalt..." -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "Tüm modelleri Seç" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "Yapı Levhasını Temizle" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "Tüm Modelleri Yeniden Yükle" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "Tüm Modelleri Düzenle" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "Seçimi Düzenle" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "Tüm Model Konumlarını Sıfırla" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "Tüm Model ve Dönüşümleri Sıfırla" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "&Dosya Aç..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "&Yeni Proje..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "Yapılandırma Klasörünü Göster" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "Görünürlük ayarını yapılandır..." @@ -5074,18 +5084,23 @@ msgid_plural "Print Selected Models With:" msgstr[0] "Seçili Modeli Şununla Yazdır:" msgstr[1] "Seçili Modelleri Şununla Yazdır:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "Seçili Modeli Çoğalt" msgstr[1] "Seçili Modelleri Çoğalt" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "Kopya Sayısı" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/zh_CN/cura.po b/resources/i18n/zh_CN/cura.po index 5d63f8552b..a2e4fd3d24 100644 --- a/resources/i18n/zh_CN/cura.po +++ b/resources/i18n/zh_CN/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: 2022-07-15 11:06+0200\n" "Last-Translator: \n" "Language-Team: \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "登录失败" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "正在为模型寻找新位置" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "正在寻找位置" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "无法在成形空间体积内放下全部模型" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "找不到位置" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "一次只能加载一个 G-code 文件。{0} 已跳过导入" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "警告" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "如果加载 G-code,则无法打开其他任何文件。{0} 已跳过导入" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "已计算" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "复制并放置模型" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "放置模型" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "放置模型" @@ -3667,17 +3667,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "删除打印机" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "通过网络打印" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "通过网络打印" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "已通过网络连接" @@ -4219,227 +4219,237 @@ msgctxt "@button" msgid "Cancel" msgstr "取消" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "显示联机故障排除" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "切换全屏" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "退出全屏" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "撤销(&U)" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "重做(&R)" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "退出(&Q)" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "3D 视图" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "正视图" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "顶视图" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "仰视图" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "左视图" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "右视图" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "配置 Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "新增打印机(&A)..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "管理打印机(&I)..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "管理材料..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "从市场添加更多材料" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "使用当前设置 / 重写值更新配置文件(&U)" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "舍弃当前更改(&D)" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "从当前设置 / 重写值创建配置文件(&C)..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "管理配置文件.." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "显示在线文档(&D)" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "BUG 反馈(&B)" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "新增功能" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "关于..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "删除所选项" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "居中所选项" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "复制所选项" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "删除模型" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "使模型居于平台中央(&N)" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "绑定模型(&G)" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "拆分模型" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "合并模型(&M)" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "复制模型(&M)…" -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "选择所有模型" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "清空打印平台" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "重新载入所有模型" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "编位所有的模型" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "为所选模型编位" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "复位所有模型的位置" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "复位所有模型的变动" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "打开文件(&O)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "新建项目(&N)..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "显示配置文件夹" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "配置设定可见性..." @@ -5062,17 +5072,22 @@ msgid "Print Selected Model With:" msgid_plural "Print Selected Models With:" msgstr[0] "打印所选模型:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "复制所选模型" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "复制个数" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" diff --git a/resources/i18n/zh_TW/cura.po b/resources/i18n/zh_TW/cura.po index 0a4522c657..f4eb2ebf6e 100644 --- a/resources/i18n/zh_TW/cura.po +++ b/resources/i18n/zh_TW/cura.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Cura 5.1\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-04-27 12:22+0000\n" +"POT-Creation-Date: 2023-05-11 14:19+0000\n" "PO-Revision-Date: 2022-01-02 19:59+0800\n" "Last-Translator: Valen Chang \n" "Language-Team: Valen Chang \n" @@ -23,22 +23,22 @@ msgctxt "@info:title" msgid "Login failed" msgstr "登入失敗" -#: cura/Arranging/ArrangeObjectsJob.py:25 +#: cura/Arranging/ArrangeObjectsJob.py:27 msgctxt "@info:status" msgid "Finding new location for objects" msgstr "正在為物件尋找新位置" -#: cura/Arranging/ArrangeObjectsJob.py:29 +#: cura/Arranging/ArrangeObjectsJob.py:31 msgctxt "@info:title" msgid "Finding Location" msgstr "尋找位置中" -#: cura/Arranging/ArrangeObjectsJob.py:42 cura/MultiplyObjectsJob.py:99 +#: cura/Arranging/ArrangeObjectsJob.py:45 cura/MultiplyObjectsJob.py:101 msgctxt "@info:status" msgid "Unable to find a location within the build volume for all objects" msgstr "無法在列印範圍內放下全部物件" -#: cura/Arranging/ArrangeObjectsJob.py:43 +#: cura/Arranging/ArrangeObjectsJob.py:46 msgctxt "@info:title" msgid "Can't Find Location" msgstr "無法找到位置" @@ -265,25 +265,25 @@ msgctxt "@info 'width', 'depth' and 'height' are variable names that must NOT be msgid "%(width).1f x %(depth).1f x %(height).1f mm" msgstr "%(width).1f x %(depth).1f x %(height).1f mm" -#: cura/CuraApplication.py:1816 +#: cura/CuraApplication.py:1817 #, python-brace-format msgctxt "@info:status" msgid "Only one G-code file can be loaded at a time. Skipped importing {0}" msgstr "一次只能載入一個 G-code 檔案。{0} 已跳過匯入" -#: cura/CuraApplication.py:1818 cura/OAuth2/AuthorizationService.py:217 +#: cura/CuraApplication.py:1819 cura/OAuth2/AuthorizationService.py:217 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:189 msgctxt "@info:title" msgid "Warning" msgstr "警告" -#: cura/CuraApplication.py:1828 +#: cura/CuraApplication.py:1829 #, python-brace-format msgctxt "@info:status" msgid "Can't open any other file if G-code is loading. Skipped importing {0}" msgstr "如果載入 G-code,則無法開啟其他任何檔案。{0} 已跳過匯入" -#: cura/CuraApplication.py:1830 cura/Settings/CuraContainerRegistry.py:156 +#: cura/CuraApplication.py:1831 cura/Settings/CuraContainerRegistry.py:156 #: cura/Settings/CuraContainerRegistry.py:166 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:153 #: plugins/RemovableDriveOutputDevice/RemovableDriveOutputDevice.py:173 @@ -410,17 +410,17 @@ msgctxt "@info:status" msgid "Calculated" msgstr "已計算" -#: cura/MultiplyObjectsJob.py:30 +#: cura/MultiplyObjectsJob.py:31 msgctxt "@info:status" msgid "Multiplying and placing objects" msgstr "正在複製並放置模型" -#: cura/MultiplyObjectsJob.py:32 +#: cura/MultiplyObjectsJob.py:33 msgctxt "@info:title" msgid "Placing Objects" msgstr "正在放置模型" -#: cura/MultiplyObjectsJob.py:100 +#: cura/MultiplyObjectsJob.py:102 msgctxt "@info:title" msgid "Placing Object" msgstr "擺放物件中" @@ -3668,17 +3668,17 @@ msgctxt "@action:button" msgid "Remove printers" msgstr "移除印表機" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:62 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 msgctxt "@action:button Preceded by 'Ready to'." msgid "Print over network" msgstr "網路連線列印" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:63 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 msgctxt "@properties:tooltip" msgid "Print over network" msgstr "網路連線列印" -#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:64 +#: plugins/UM3NetworkPrinting/src/Network/LocalClusterOutputDevice.py:65 msgctxt "@info:status" msgid "Connected over the network" msgstr "透過網路連接" @@ -4220,227 +4220,237 @@ msgctxt "@button" msgid "Cancel" msgstr "取消" -#: resources/qml/Actions.qml:81 +#: resources/qml/Actions.qml:83 msgctxt "@action:inmenu" msgid "Show Online Troubleshooting" msgstr "" -#: resources/qml/Actions.qml:88 +#: resources/qml/Actions.qml:90 msgctxt "@action:inmenu" msgid "Toggle Full Screen" msgstr "切換全螢幕" -#: resources/qml/Actions.qml:96 +#: resources/qml/Actions.qml:98 msgctxt "@action:inmenu" msgid "Exit Full Screen" msgstr "離開全螢幕" -#: resources/qml/Actions.qml:103 +#: resources/qml/Actions.qml:105 msgctxt "@action:inmenu menubar:edit" msgid "&Undo" msgstr "復原(&U)" -#: resources/qml/Actions.qml:113 +#: resources/qml/Actions.qml:115 msgctxt "@action:inmenu menubar:edit" msgid "&Redo" msgstr "取消復原(&R)" -#: resources/qml/Actions.qml:131 +#: resources/qml/Actions.qml:133 msgctxt "@action:inmenu menubar:file" msgid "&Quit" msgstr "退出(&Q)" -#: resources/qml/Actions.qml:139 +#: resources/qml/Actions.qml:141 msgctxt "@action:inmenu menubar:view" msgid "3D View" msgstr "立體圖" -#: resources/qml/Actions.qml:146 +#: resources/qml/Actions.qml:148 msgctxt "@action:inmenu menubar:view" msgid "Front View" msgstr "前視圖" -#: resources/qml/Actions.qml:153 +#: resources/qml/Actions.qml:155 msgctxt "@action:inmenu menubar:view" msgid "Top View" msgstr "上視圖" -#: resources/qml/Actions.qml:160 +#: resources/qml/Actions.qml:162 msgctxt "@action:inmenu menubar:view" msgid "Bottom View" msgstr "下視圖" -#: resources/qml/Actions.qml:167 +#: resources/qml/Actions.qml:169 msgctxt "@action:inmenu menubar:view" msgid "Left Side View" msgstr "左視圖" -#: resources/qml/Actions.qml:174 +#: resources/qml/Actions.qml:176 msgctxt "@action:inmenu menubar:view" msgid "Right Side View" msgstr "右視圖" -#: resources/qml/Actions.qml:188 +#: resources/qml/Actions.qml:190 msgctxt "@action:inmenu" msgid "Configure Cura..." msgstr "設定 Cura..." -#: resources/qml/Actions.qml:197 +#: resources/qml/Actions.qml:199 msgctxt "@action:inmenu menubar:printer" msgid "&Add Printer..." msgstr "新增印表機(&A)..." -#: resources/qml/Actions.qml:203 +#: resources/qml/Actions.qml:205 msgctxt "@action:inmenu menubar:printer" msgid "Manage Pr&inters..." msgstr "管理印表機(&I)..." -#: resources/qml/Actions.qml:210 +#: resources/qml/Actions.qml:212 msgctxt "@action:inmenu" msgid "Manage Materials..." msgstr "管理線材..." -#: resources/qml/Actions.qml:218 +#: resources/qml/Actions.qml:220 msgctxt "@action:inmenu Marketplace is a brand name of UltiMaker's, so don't translate." msgid "Add more materials from Marketplace" msgstr "從市集增加更多線材" -#: resources/qml/Actions.qml:225 +#: resources/qml/Actions.qml:227 msgctxt "@action:inmenu menubar:profile" msgid "&Update profile with current settings/overrides" msgstr "使用目前設定 / 覆寫更新列印參數(&U)" -#: resources/qml/Actions.qml:233 +#: resources/qml/Actions.qml:235 msgctxt "@action:inmenu menubar:profile" msgid "&Discard current changes" msgstr "捨棄目前更改(&D)" -#: resources/qml/Actions.qml:245 +#: resources/qml/Actions.qml:247 msgctxt "@action:inmenu menubar:profile" msgid "&Create profile from current settings/overrides..." msgstr "從目前設定 / 覆寫值建立列印參數(&C)..." -#: resources/qml/Actions.qml:251 +#: resources/qml/Actions.qml:253 msgctxt "@action:inmenu menubar:profile" msgid "Manage Profiles..." msgstr "管理列印參數.." -#: resources/qml/Actions.qml:259 +#: resources/qml/Actions.qml:261 msgctxt "@action:inmenu menubar:help" msgid "Show Online &Documentation" msgstr "顯示線上說明文件(&D)" -#: resources/qml/Actions.qml:267 +#: resources/qml/Actions.qml:269 msgctxt "@action:inmenu menubar:help" msgid "Report a &Bug" msgstr "BUG 回報(&B)" -#: resources/qml/Actions.qml:275 +#: resources/qml/Actions.qml:277 msgctxt "@action:inmenu menubar:help" msgid "What's New" msgstr "新功能" -#: resources/qml/Actions.qml:289 +#: resources/qml/Actions.qml:291 msgctxt "@action:inmenu menubar:help" msgid "About..." msgstr "關於..." -#: resources/qml/Actions.qml:296 +#: resources/qml/Actions.qml:298 msgctxt "@action:inmenu menubar:edit" msgid "Delete Selected" msgstr "刪除選取" -#: resources/qml/Actions.qml:306 +#: resources/qml/Actions.qml:308 msgctxt "@action:inmenu menubar:edit" msgid "Center Selected" msgstr "置中選取" -#: resources/qml/Actions.qml:315 +#: resources/qml/Actions.qml:317 msgctxt "@action:inmenu menubar:edit" msgid "Multiply Selected" msgstr "複製選取" -#: resources/qml/Actions.qml:324 +#: resources/qml/Actions.qml:326 msgctxt "@action:inmenu" msgid "Delete Model" msgstr "刪除模型" -#: resources/qml/Actions.qml:332 +#: resources/qml/Actions.qml:334 msgctxt "@action:inmenu" msgid "Ce&nter Model on Platform" msgstr "將模型置中(&N)" -#: resources/qml/Actions.qml:338 +#: resources/qml/Actions.qml:340 msgctxt "@action:inmenu menubar:edit" msgid "&Group Models" msgstr "群組模型(&G)" -#: resources/qml/Actions.qml:358 +#: resources/qml/Actions.qml:360 msgctxt "@action:inmenu menubar:edit" msgid "Ungroup Models" msgstr "取消模型群組" -#: resources/qml/Actions.qml:368 +#: resources/qml/Actions.qml:370 msgctxt "@action:inmenu menubar:edit" msgid "&Merge Models" msgstr "結合模型(&M)" -#: resources/qml/Actions.qml:378 +#: resources/qml/Actions.qml:380 msgctxt "@action:inmenu" msgid "&Multiply Model..." msgstr "複製模型...(&M)" -#: resources/qml/Actions.qml:385 +#: resources/qml/Actions.qml:387 msgctxt "@action:inmenu menubar:edit" msgid "Select All Models" msgstr "選擇所有模型" -#: resources/qml/Actions.qml:395 +#: resources/qml/Actions.qml:397 msgctxt "@action:inmenu menubar:edit" msgid "Clear Build Plate" msgstr "清空列印平台" -#: resources/qml/Actions.qml:405 +#: resources/qml/Actions.qml:407 msgctxt "@action:inmenu menubar:file" msgid "Reload All Models" msgstr "重新載入所有模型" -#: resources/qml/Actions.qml:414 +#: resources/qml/Actions.qml:416 msgctxt "@action:inmenu menubar:edit" msgid "Arrange All Models" msgstr "排列所有模型" -#: resources/qml/Actions.qml:422 +#: resources/qml/Actions.qml:424 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange All Models Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:431 msgctxt "@action:inmenu menubar:edit" msgid "Arrange Selection" msgstr "排列所選模型" -#: resources/qml/Actions.qml:429 +#: resources/qml/Actions.qml:438 +msgctxt "@action:inmenu menubar:edit" +msgid "Arrange Selection Without Rotation" +msgstr "" + +#: resources/qml/Actions.qml:445 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Positions" msgstr "重置所有模型位置" -#: resources/qml/Actions.qml:436 +#: resources/qml/Actions.qml:452 msgctxt "@action:inmenu menubar:edit" msgid "Reset All Model Transformations" msgstr "重置所有模型旋轉" -#: resources/qml/Actions.qml:445 +#: resources/qml/Actions.qml:461 msgctxt "@action:inmenu menubar:file" msgid "&Open File(s)..." msgstr "開啟檔案(&O)..." -#: resources/qml/Actions.qml:455 +#: resources/qml/Actions.qml:471 msgctxt "@action:inmenu menubar:file" msgid "&New Project..." msgstr "新建專案(&N)..." -#: resources/qml/Actions.qml:462 +#: resources/qml/Actions.qml:478 msgctxt "@action:inmenu menubar:help" msgid "Show Configuration Folder" msgstr "顯示設定資料夾" -#: resources/qml/Actions.qml:469 resources/qml/Settings/SettingView.qml:476 +#: resources/qml/Actions.qml:485 resources/qml/Settings/SettingView.qml:476 msgctxt "@action:menu" msgid "Configure setting visibility..." msgstr "參數顯示設定..." @@ -5060,17 +5070,22 @@ msgid "Print Selected Model With:" msgid_plural "Print Selected Models With:" msgstr[0] "列印所選模型:" -#: resources/qml/Menus/ContextMenu.qml:92 +#: resources/qml/Menus/ContextMenu.qml:93 msgctxt "@title:window" msgid "Multiply Selected Model" msgid_plural "Multiply Selected Models" msgstr[0] "複製所選模型" -#: resources/qml/Menus/ContextMenu.qml:123 +#: resources/qml/Menus/ContextMenu.qml:128 msgctxt "@label" msgid "Number of Copies" msgstr "複製個數" +#: resources/qml/Menus/ContextMenu.qml:149 +msgctxt "@label" +msgid "Lock Rotation" +msgstr "" + #: resources/qml/Menus/EditMenu.qml:12 msgctxt "@title:menu menubar:toplevel" msgid "&Edit" From 1c07ba81f4f9ddfb5509729dc6da02f256c88c8f Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Fri, 12 May 2023 09:23:03 +0200 Subject: [PATCH 03/30] Add shortcut for "arrange without rotation" action CURA-7951 --- resources/qml/Actions.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index eb9f1c7f21..8a4826e149 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -423,6 +423,7 @@ Item id: arrangeAllLockAction text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models Without Rotation") onTriggered: Printer.arrangeAll(true) + shortcut: "Shift+Ctrl+R" } Action From 209162fbce5dc0e5b2ff934900623691c46df233 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 21 Aug 2023 17:31:29 +0200 Subject: [PATCH 04/30] multiplying objects place them in a grid CURA-7951 --- cura/Arranging/GridArrange.py | 185 ++++++++++++++++++++++++++++++++++ cura/MultiplyObjectsJob.py | 18 ++-- 2 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 cura/Arranging/GridArrange.py diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py new file mode 100644 index 0000000000..543971c5b2 --- /dev/null +++ b/cura/Arranging/GridArrange.py @@ -0,0 +1,185 @@ +import math +from typing import List, TYPE_CHECKING, Optional, Tuple, Set + +from UM.Application import Application +from UM.Math import AxisAlignedBox +from UM.Math.Vector import Vector +from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation +from UM.Operations.GroupedOperation import GroupedOperation +from UM.Operations.TranslateOperation import TranslateOperation + + +class GridArrange: + offset_x: float = 10 + offset_y: float = 10 + + _grid_width: float + _grid_height: float + + _nodes_to_arrange: List["SceneNode"] + _fixed_nodes: List["SceneNode"] + _build_volume_bounding_box = AxisAlignedBox + + def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = []): + print("len(nodes_to_arrange)", len(nodes_to_arrange)) + self._nodes_to_arrange = nodes_to_arrange + self._build_volume_bounding_box = build_volume.getBoundingBox() + self._fixed_nodes = fixed_nodes + + def arrange(self) -> Tuple[GroupedOperation, int]: + self._grid_width = 0 + self._grid_height = 0 + for node in self._nodes_to_arrange: + bounding_box = node.getBoundingBox() + self._grid_width = max(self._grid_width, bounding_box.width) + self._grid_height = max(self._grid_height, bounding_box.depth) + + # Find grid indexes that intersect with fixed objects + fixed_nodes_grid_ids = set() + for node in self._fixed_nodes: + fixed_nodes_grid_ids = fixed_nodes_grid_ids.union(self.intersectingGridIdxInclusive(node.getBoundingBox())) + + build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) + allowed_grid_idx = build_plate_grid_ids.difference(fixed_nodes_grid_ids) + + # Find the sequence in which items are placed + coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left + coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back + grid_build_plate_center_x, grid_build_plate_center_y = self.coordSpaceToGridSpace(coord_build_plate_center_x, coord_build_plate_center_y) + + def distToCenter(grid_id: Tuple[int, int]) -> float: + grid_x, grid_y = grid_id + distance_squared = (grid_build_plate_center_x - grid_x) ** 2 + (grid_build_plate_center_y - grid_y) ** 2 + return distance_squared + + sequence: List[Tuple[int, int]] = list(allowed_grid_idx) + sequence.sort(key=distToCenter) + scene_root = Application.getInstance().getController().getScene().getRoot() + grouped_operation = GroupedOperation() + + for grid_id, node in zip(sequence, self._nodes_to_arrange): + grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) + grid_x, grid_y = grid_id + + coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) + center_grid_x = coord_grid_x+(0.5 * self._grid_width) + center_grid_y = coord_grid_y+(0.5 * self._grid_height) + + bounding_box = node.getBoundingBox() + center_node_x = (bounding_box.left + bounding_box.right) * 0.5 + center_node_y = (bounding_box.back + bounding_box.front) * 0.5 + + delta_x = center_grid_x - center_node_x + delta_y = center_grid_y - center_node_y + grouped_operation.addOperation(TranslateOperation(node, Vector(delta_x, 0, delta_y))) + + self.drawDebugSvg() + + return grouped_operation, 0 + + def getGridCornerPoints(self, bounding_box: "BoundingVolume") -> Tuple[float, float, float, float]: + coord_x1 = bounding_box.left + coord_x2 = bounding_box.right + coord_y1 = bounding_box.back + coord_y2 = bounding_box.front + grid_x1, grid_y1 = self.coordSpaceToGridSpace(coord_x1, coord_y1) + grid_x2, grid_y2 = self.coordSpaceToGridSpace(coord_x2, coord_y2) + return grid_x1, grid_y1, grid_x2, grid_y2 + + def intersectingGridIdxInclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: + grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) + grid_idx = set() + for grid_x in range(math.floor(grid_x1), math.ceil(grid_x2)): + for grid_y in range(math.floor(grid_y1), math.ceil(grid_y2)): + grid_idx.add((grid_x, grid_y)) + return grid_idx + + def intersectingGridIdxExclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: + grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) + grid_idx = set() + for grid_x in range(math.ceil(grid_x1), math.floor(grid_x2)): + for grid_y in range(math.ceil(grid_y1), math.floor(grid_y2)): + grid_idx.add((grid_x, grid_y)) + return grid_idx + + def gridSpaceToCoordSpace(self, x: float, y: float) -> Tuple[float, float]: + grid_x = x * (self._grid_width + self.offset_x) + self._build_volume_bounding_box.left + grid_y = y * (self._grid_height + self.offset_y) + self._build_volume_bounding_box.back + return grid_x, grid_y + + def coordSpaceToGridSpace(self, grid_x: float, grid_y: float) -> Tuple[float, float]: + coord_x = (grid_x - self._build_volume_bounding_box.left) / (self._grid_width + self.offset_x) + coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self.offset_y) + return coord_x, coord_y + + def drawDebugSvg(self): + with open("Builvolume_test.svg", "w") as f: + build_volume_bounding_box = self._build_volume_bounding_box + + f.write( + f"\n") + + f.write( + f""" + + """) + + for grid_x in range(-10, 10): + for grid_y in range(-10, 10): + # if (grid_x, grid_y) in intersecting_grid_idx: + # fill_color = "red" + # elif (grid_x, grid_y) in build_plate_grid_idx: + # fill_color = "green" + # else: + # fill_color = "orange" + coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) + f.write( + f""" + + """) + f.write(f""" + + {grid_x},{grid_y} + + """) + for node in self._fixed_nodes: + bounding_box = node.getBoundingBox() + f.write(f""" + + """) + for node in self._nodes_to_arrange: + bounding_box = node.getBoundingBox() + f.write(f""" + + """) + f.write(f"") diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index ff4f362b4c..e1cead7557 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -14,6 +14,7 @@ from UM.Operations.TranslateOperation import TranslateOperation from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog +from cura.Arranging.GridArrange import GridArrange from cura.Arranging.Nest2DArrange import arrange, createGroupOperationForArrange i18n_catalog = i18nCatalog("cura") @@ -77,12 +78,17 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True group_operation = GroupedOperation() if nodes: - group_operation, not_fit_count = createGroupOperationForArrange(nodes, - Application.getInstance().getBuildVolume(), - fixed_nodes, - factor=10000, - add_new_nodes_in_scene=True, - lock_rotation=self._lock_rotation) + grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(), + fixed_nodes) + + group_operation, not_fit_count = grid_arrange.arrange() + print("group_operation", group_operation) + # group_operation, not_fit_count = createGroupOperationForArrange(nodes, + # Application.getInstance().getBuildVolume(), + # fixed_nodes, + # factor=10000, + # add_new_nodes_in_scene=True, + # lock_rotation=self._lock_rotation) found_solution_for_all = not_fit_count == 0 if nodes_to_add_without_arrange: From df69ccadb585fb86291861b1dfd3015dadfd0182 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 21 Aug 2023 18:45:40 +0200 Subject: [PATCH 05/30] Revert "multiplying objects place them in a grid" This reverts commit 209162fbce5dc0e5b2ff934900623691c46df233. --- cura/Arranging/GridArrange.py | 185 ---------------------------------- cura/MultiplyObjectsJob.py | 18 ++-- 2 files changed, 6 insertions(+), 197 deletions(-) delete mode 100644 cura/Arranging/GridArrange.py diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py deleted file mode 100644 index 543971c5b2..0000000000 --- a/cura/Arranging/GridArrange.py +++ /dev/null @@ -1,185 +0,0 @@ -import math -from typing import List, TYPE_CHECKING, Optional, Tuple, Set - -from UM.Application import Application -from UM.Math import AxisAlignedBox -from UM.Math.Vector import Vector -from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation -from UM.Operations.GroupedOperation import GroupedOperation -from UM.Operations.TranslateOperation import TranslateOperation - - -class GridArrange: - offset_x: float = 10 - offset_y: float = 10 - - _grid_width: float - _grid_height: float - - _nodes_to_arrange: List["SceneNode"] - _fixed_nodes: List["SceneNode"] - _build_volume_bounding_box = AxisAlignedBox - - def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = []): - print("len(nodes_to_arrange)", len(nodes_to_arrange)) - self._nodes_to_arrange = nodes_to_arrange - self._build_volume_bounding_box = build_volume.getBoundingBox() - self._fixed_nodes = fixed_nodes - - def arrange(self) -> Tuple[GroupedOperation, int]: - self._grid_width = 0 - self._grid_height = 0 - for node in self._nodes_to_arrange: - bounding_box = node.getBoundingBox() - self._grid_width = max(self._grid_width, bounding_box.width) - self._grid_height = max(self._grid_height, bounding_box.depth) - - # Find grid indexes that intersect with fixed objects - fixed_nodes_grid_ids = set() - for node in self._fixed_nodes: - fixed_nodes_grid_ids = fixed_nodes_grid_ids.union(self.intersectingGridIdxInclusive(node.getBoundingBox())) - - build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) - allowed_grid_idx = build_plate_grid_ids.difference(fixed_nodes_grid_ids) - - # Find the sequence in which items are placed - coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left - coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back - grid_build_plate_center_x, grid_build_plate_center_y = self.coordSpaceToGridSpace(coord_build_plate_center_x, coord_build_plate_center_y) - - def distToCenter(grid_id: Tuple[int, int]) -> float: - grid_x, grid_y = grid_id - distance_squared = (grid_build_plate_center_x - grid_x) ** 2 + (grid_build_plate_center_y - grid_y) ** 2 - return distance_squared - - sequence: List[Tuple[int, int]] = list(allowed_grid_idx) - sequence.sort(key=distToCenter) - scene_root = Application.getInstance().getController().getScene().getRoot() - grouped_operation = GroupedOperation() - - for grid_id, node in zip(sequence, self._nodes_to_arrange): - grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) - grid_x, grid_y = grid_id - - coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) - center_grid_x = coord_grid_x+(0.5 * self._grid_width) - center_grid_y = coord_grid_y+(0.5 * self._grid_height) - - bounding_box = node.getBoundingBox() - center_node_x = (bounding_box.left + bounding_box.right) * 0.5 - center_node_y = (bounding_box.back + bounding_box.front) * 0.5 - - delta_x = center_grid_x - center_node_x - delta_y = center_grid_y - center_node_y - grouped_operation.addOperation(TranslateOperation(node, Vector(delta_x, 0, delta_y))) - - self.drawDebugSvg() - - return grouped_operation, 0 - - def getGridCornerPoints(self, bounding_box: "BoundingVolume") -> Tuple[float, float, float, float]: - coord_x1 = bounding_box.left - coord_x2 = bounding_box.right - coord_y1 = bounding_box.back - coord_y2 = bounding_box.front - grid_x1, grid_y1 = self.coordSpaceToGridSpace(coord_x1, coord_y1) - grid_x2, grid_y2 = self.coordSpaceToGridSpace(coord_x2, coord_y2) - return grid_x1, grid_y1, grid_x2, grid_y2 - - def intersectingGridIdxInclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: - grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) - grid_idx = set() - for grid_x in range(math.floor(grid_x1), math.ceil(grid_x2)): - for grid_y in range(math.floor(grid_y1), math.ceil(grid_y2)): - grid_idx.add((grid_x, grid_y)) - return grid_idx - - def intersectingGridIdxExclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: - grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) - grid_idx = set() - for grid_x in range(math.ceil(grid_x1), math.floor(grid_x2)): - for grid_y in range(math.ceil(grid_y1), math.floor(grid_y2)): - grid_idx.add((grid_x, grid_y)) - return grid_idx - - def gridSpaceToCoordSpace(self, x: float, y: float) -> Tuple[float, float]: - grid_x = x * (self._grid_width + self.offset_x) + self._build_volume_bounding_box.left - grid_y = y * (self._grid_height + self.offset_y) + self._build_volume_bounding_box.back - return grid_x, grid_y - - def coordSpaceToGridSpace(self, grid_x: float, grid_y: float) -> Tuple[float, float]: - coord_x = (grid_x - self._build_volume_bounding_box.left) / (self._grid_width + self.offset_x) - coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self.offset_y) - return coord_x, coord_y - - def drawDebugSvg(self): - with open("Builvolume_test.svg", "w") as f: - build_volume_bounding_box = self._build_volume_bounding_box - - f.write( - f"\n") - - f.write( - f""" - - """) - - for grid_x in range(-10, 10): - for grid_y in range(-10, 10): - # if (grid_x, grid_y) in intersecting_grid_idx: - # fill_color = "red" - # elif (grid_x, grid_y) in build_plate_grid_idx: - # fill_color = "green" - # else: - # fill_color = "orange" - coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) - f.write( - f""" - - """) - f.write(f""" - - {grid_x},{grid_y} - - """) - for node in self._fixed_nodes: - bounding_box = node.getBoundingBox() - f.write(f""" - - """) - for node in self._nodes_to_arrange: - bounding_box = node.getBoundingBox() - f.write(f""" - - """) - f.write(f"") diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index e1cead7557..ff4f362b4c 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -14,7 +14,6 @@ from UM.Operations.TranslateOperation import TranslateOperation from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog -from cura.Arranging.GridArrange import GridArrange from cura.Arranging.Nest2DArrange import arrange, createGroupOperationForArrange i18n_catalog = i18nCatalog("cura") @@ -78,17 +77,12 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True group_operation = GroupedOperation() if nodes: - grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(), - fixed_nodes) - - group_operation, not_fit_count = grid_arrange.arrange() - print("group_operation", group_operation) - # group_operation, not_fit_count = createGroupOperationForArrange(nodes, - # Application.getInstance().getBuildVolume(), - # fixed_nodes, - # factor=10000, - # add_new_nodes_in_scene=True, - # lock_rotation=self._lock_rotation) + group_operation, not_fit_count = createGroupOperationForArrange(nodes, + Application.getInstance().getBuildVolume(), + fixed_nodes, + factor=10000, + add_new_nodes_in_scene=True, + lock_rotation=self._lock_rotation) found_solution_for_all = not_fit_count == 0 if nodes_to_add_without_arrange: From b91ebcbb364b14d2aa6a8d894fcbc27a2bb06538 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 21 Aug 2023 18:51:08 +0200 Subject: [PATCH 06/30] multiplying objects place them in a grid Co-authored-by: Casper Lamboo CURA-7951 --- cura/Arranging/GridArrange.py | 185 ++++++++++++++++++++++++++++++++++ cura/MultiplyObjectsJob.py | 18 ++-- 2 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 cura/Arranging/GridArrange.py diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py new file mode 100644 index 0000000000..543971c5b2 --- /dev/null +++ b/cura/Arranging/GridArrange.py @@ -0,0 +1,185 @@ +import math +from typing import List, TYPE_CHECKING, Optional, Tuple, Set + +from UM.Application import Application +from UM.Math import AxisAlignedBox +from UM.Math.Vector import Vector +from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation +from UM.Operations.GroupedOperation import GroupedOperation +from UM.Operations.TranslateOperation import TranslateOperation + + +class GridArrange: + offset_x: float = 10 + offset_y: float = 10 + + _grid_width: float + _grid_height: float + + _nodes_to_arrange: List["SceneNode"] + _fixed_nodes: List["SceneNode"] + _build_volume_bounding_box = AxisAlignedBox + + def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = []): + print("len(nodes_to_arrange)", len(nodes_to_arrange)) + self._nodes_to_arrange = nodes_to_arrange + self._build_volume_bounding_box = build_volume.getBoundingBox() + self._fixed_nodes = fixed_nodes + + def arrange(self) -> Tuple[GroupedOperation, int]: + self._grid_width = 0 + self._grid_height = 0 + for node in self._nodes_to_arrange: + bounding_box = node.getBoundingBox() + self._grid_width = max(self._grid_width, bounding_box.width) + self._grid_height = max(self._grid_height, bounding_box.depth) + + # Find grid indexes that intersect with fixed objects + fixed_nodes_grid_ids = set() + for node in self._fixed_nodes: + fixed_nodes_grid_ids = fixed_nodes_grid_ids.union(self.intersectingGridIdxInclusive(node.getBoundingBox())) + + build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) + allowed_grid_idx = build_plate_grid_ids.difference(fixed_nodes_grid_ids) + + # Find the sequence in which items are placed + coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left + coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back + grid_build_plate_center_x, grid_build_plate_center_y = self.coordSpaceToGridSpace(coord_build_plate_center_x, coord_build_plate_center_y) + + def distToCenter(grid_id: Tuple[int, int]) -> float: + grid_x, grid_y = grid_id + distance_squared = (grid_build_plate_center_x - grid_x) ** 2 + (grid_build_plate_center_y - grid_y) ** 2 + return distance_squared + + sequence: List[Tuple[int, int]] = list(allowed_grid_idx) + sequence.sort(key=distToCenter) + scene_root = Application.getInstance().getController().getScene().getRoot() + grouped_operation = GroupedOperation() + + for grid_id, node in zip(sequence, self._nodes_to_arrange): + grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) + grid_x, grid_y = grid_id + + coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) + center_grid_x = coord_grid_x+(0.5 * self._grid_width) + center_grid_y = coord_grid_y+(0.5 * self._grid_height) + + bounding_box = node.getBoundingBox() + center_node_x = (bounding_box.left + bounding_box.right) * 0.5 + center_node_y = (bounding_box.back + bounding_box.front) * 0.5 + + delta_x = center_grid_x - center_node_x + delta_y = center_grid_y - center_node_y + grouped_operation.addOperation(TranslateOperation(node, Vector(delta_x, 0, delta_y))) + + self.drawDebugSvg() + + return grouped_operation, 0 + + def getGridCornerPoints(self, bounding_box: "BoundingVolume") -> Tuple[float, float, float, float]: + coord_x1 = bounding_box.left + coord_x2 = bounding_box.right + coord_y1 = bounding_box.back + coord_y2 = bounding_box.front + grid_x1, grid_y1 = self.coordSpaceToGridSpace(coord_x1, coord_y1) + grid_x2, grid_y2 = self.coordSpaceToGridSpace(coord_x2, coord_y2) + return grid_x1, grid_y1, grid_x2, grid_y2 + + def intersectingGridIdxInclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: + grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) + grid_idx = set() + for grid_x in range(math.floor(grid_x1), math.ceil(grid_x2)): + for grid_y in range(math.floor(grid_y1), math.ceil(grid_y2)): + grid_idx.add((grid_x, grid_y)) + return grid_idx + + def intersectingGridIdxExclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: + grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) + grid_idx = set() + for grid_x in range(math.ceil(grid_x1), math.floor(grid_x2)): + for grid_y in range(math.ceil(grid_y1), math.floor(grid_y2)): + grid_idx.add((grid_x, grid_y)) + return grid_idx + + def gridSpaceToCoordSpace(self, x: float, y: float) -> Tuple[float, float]: + grid_x = x * (self._grid_width + self.offset_x) + self._build_volume_bounding_box.left + grid_y = y * (self._grid_height + self.offset_y) + self._build_volume_bounding_box.back + return grid_x, grid_y + + def coordSpaceToGridSpace(self, grid_x: float, grid_y: float) -> Tuple[float, float]: + coord_x = (grid_x - self._build_volume_bounding_box.left) / (self._grid_width + self.offset_x) + coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self.offset_y) + return coord_x, coord_y + + def drawDebugSvg(self): + with open("Builvolume_test.svg", "w") as f: + build_volume_bounding_box = self._build_volume_bounding_box + + f.write( + f"\n") + + f.write( + f""" + + """) + + for grid_x in range(-10, 10): + for grid_y in range(-10, 10): + # if (grid_x, grid_y) in intersecting_grid_idx: + # fill_color = "red" + # elif (grid_x, grid_y) in build_plate_grid_idx: + # fill_color = "green" + # else: + # fill_color = "orange" + coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) + f.write( + f""" + + """) + f.write(f""" + + {grid_x},{grid_y} + + """) + for node in self._fixed_nodes: + bounding_box = node.getBoundingBox() + f.write(f""" + + """) + for node in self._nodes_to_arrange: + bounding_box = node.getBoundingBox() + f.write(f""" + + """) + f.write(f"") diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index ff4f362b4c..e1cead7557 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -14,6 +14,7 @@ from UM.Operations.TranslateOperation import TranslateOperation from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog +from cura.Arranging.GridArrange import GridArrange from cura.Arranging.Nest2DArrange import arrange, createGroupOperationForArrange i18n_catalog = i18nCatalog("cura") @@ -77,12 +78,17 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True group_operation = GroupedOperation() if nodes: - group_operation, not_fit_count = createGroupOperationForArrange(nodes, - Application.getInstance().getBuildVolume(), - fixed_nodes, - factor=10000, - add_new_nodes_in_scene=True, - lock_rotation=self._lock_rotation) + grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(), + fixed_nodes) + + group_operation, not_fit_count = grid_arrange.arrange() + print("group_operation", group_operation) + # group_operation, not_fit_count = createGroupOperationForArrange(nodes, + # Application.getInstance().getBuildVolume(), + # fixed_nodes, + # factor=10000, + # add_new_nodes_in_scene=True, + # lock_rotation=self._lock_rotation) found_solution_for_all = not_fit_count == 0 if nodes_to_add_without_arrange: From b62725b4f0289ed672281d4b913d708f4de3234a Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 22 Aug 2023 10:30:51 +0200 Subject: [PATCH 07/30] copy_paste in a grid arrange all objects in grid place in grid Co-authored-by: Casper Lamboo CURA-7951 --- cura/Arranging/ArrangeObjectsJob.py | 15 +++++++++++---- cura/Arranging/GridArrange.py | 8 +++++++- cura/CuraActions.py | 12 ++++++------ cura/CuraApplication.py | 10 +++++----- cura/MultiplyObjectsJob.py | 24 +++++++++++------------- resources/qml/Actions.qml | 6 +++--- resources/qml/Menus/ContextMenu.qml | 8 ++++---- 7 files changed, 47 insertions(+), 36 deletions(-) diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index f938b44d1f..d122098565 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -8,6 +8,7 @@ from UM.Logger import Logger from UM.Message import Message from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog +from cura.Arranging.GridArrange import GridArrange from cura.Arranging.Nest2DArrange import arrange i18n_catalog = i18nCatalog("cura") @@ -15,12 +16,12 @@ i18n_catalog = i18nCatalog("cura") class ArrangeObjectsJob(Job): def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset=8, - lock_rotation: bool = False) -> None: + grid_arrange: bool = False) -> None: super().__init__() self._nodes = nodes self._fixed_nodes = fixed_nodes self._min_offset = min_offset - self._lock_rotation = lock_rotation + self._grid_arrange = grid_arrange def run(self): found_solution_for_all = False @@ -32,8 +33,14 @@ class ArrangeObjectsJob(Job): status_message.show() try: - found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, - lock_rotation=self._lock_rotation) + + if self._grid_arrange: + grid_arrange = GridArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + found_solution_for_all = grid_arrange.arrange() + + else: + found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + except: # If the thread crashes, the message should still close Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 543971c5b2..0d76ecff52 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -26,7 +26,13 @@ class GridArrange: self._build_volume_bounding_box = build_volume.getBoundingBox() self._fixed_nodes = fixed_nodes - def arrange(self) -> Tuple[GroupedOperation, int]: + def arrange(self)-> bool: + + grouped_operation, not_fit_count = self.createGroupOperationForArrange() + grouped_operation.push() + return not_fit_count == 0 + + def createGroupOperationForArrange(self) -> Tuple[GroupedOperation, int]: self._grid_width = 0 self._grid_height = 0 for node in self._nodes_to_arrange: diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 3567c1532f..1b98bdddd8 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -18,6 +18,7 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.TranslateOperation import TranslateOperation import cura.CuraApplication +from cura.Arranging.GridArrange import GridArrange from cura.Operations.SetParentOperation import SetParentOperation from cura.MultiplyObjectsJob import MultiplyObjectsJob from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation @@ -84,16 +85,16 @@ class CuraActions(QObject): operation.push() @pyqtSlot(int, bool) - def multiplySelection(self, count: int, lock_rotation: bool) -> None: + def multiplySelection(self, count: int, grid_placement: bool) -> None: """Multiply all objects in the selection :param count: The number of times to multiply the selection. - :param lock_rotation: If set to true the orientation of the object will remain the same + :param grid_placement: If set to true objects are placed in a grid """ min_offset = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset=max(min_offset, 8), - lock_rotation=lock_rotation) + grid_arrange=grid_placement) job.start() @pyqtSlot() @@ -231,9 +232,8 @@ class CuraActions(QObject): if node.callDecoration("isSliceable"): fixed_nodes.append(node) # Add the new nodes to the scene, and arrange them - group_operation, not_fit_count = createGroupOperationForArrange(nodes, application.getBuildVolume(), - fixed_nodes, factor=10000, - add_new_nodes_in_scene=True) + grid_arrange = GridArrange(nodes, application.getBuildVolume(), fixed_nodes) + group_operation, not_fit_count = grid_arrange.createGroupOperationForArrange() group_operation.push() # deselect currently selected nodes, and select the new nodes diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 82d009e436..c6c0ed3cd2 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1441,7 +1441,7 @@ class CuraApplication(QtApplication): # Single build plate @pyqtSlot(bool) - def arrangeAll(self, lock_rotation: bool) -> None: + def arrangeAll(self, grid_arrangement: bool) -> None: nodes_to_arrange = [] active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate locked_nodes = [] @@ -1471,18 +1471,18 @@ class CuraApplication(QtApplication): locked_nodes.append(node) else: nodes_to_arrange.append(node) - self.arrange(nodes_to_arrange, locked_nodes, lock_rotation) + self.arrange(nodes_to_arrange, locked_nodes, grid_arrangement) - def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], lock_rotation: bool = False) -> None: + def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], grid_arrangement: bool = False) -> None: """Arrange a set of nodes given a set of fixed nodes :param nodes: nodes that we have to place :param fixed_nodes: nodes that are placed in the arranger before finding spots for nodes - :param lock_rotation: If set to true the orientation of the object will remain the same + :param grid_arrangement: If set to true if objects are to be placed in a grid """ min_offset = self.getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors - job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset=max(min_offset, 8), lock_rotation=lock_rotation) + job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset=max(min_offset, 8), grid_arrange =grid_arrangement) job.start() @pyqtSlot() diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index e1cead7557..6df80eb01c 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -21,12 +21,12 @@ i18n_catalog = i18nCatalog("cura") class MultiplyObjectsJob(Job): - def __init__(self, objects, count: int, min_offset: int = 8, lock_rotation: bool = False): + def __init__(self, objects, count: int, min_offset: int = 8, grid_arrange: bool = False): super().__init__() self._objects = objects self._count: int = count self._min_offset: int = min_offset - self._lock_rotation: bool = lock_rotation + self._grid_arrange: bool = grid_arrange def run(self) -> None: status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime = 0, @@ -78,18 +78,16 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True group_operation = GroupedOperation() if nodes: - grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(), - fixed_nodes) + if(self._grid_arrange): + grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(),fixed_nodes) + group_operation, not_fit_count = grid_arrange.createGroupOperationForArrange() - group_operation, not_fit_count = grid_arrange.arrange() - print("group_operation", group_operation) - # group_operation, not_fit_count = createGroupOperationForArrange(nodes, - # Application.getInstance().getBuildVolume(), - # fixed_nodes, - # factor=10000, - # add_new_nodes_in_scene=True, - # lock_rotation=self._lock_rotation) - found_solution_for_all = not_fit_count == 0 + else: + group_operation, not_fit_count = createGroupOperationForArrange(nodes, + Application.getInstance().getBuildVolume(), + fixed_nodes, + factor=10000, + add_new_nodes_in_scene=True) if nodes_to_add_without_arrange: for nested_node in nodes_to_add_without_arrange: diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index e47f797195..1c84ec66b7 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -41,7 +41,7 @@ Item property alias deleteAll: deleteAllAction property alias reloadAll: reloadAllAction property alias arrangeAll: arrangeAllAction - property alias arrangeAllLock: arrangeAllLockAction + property alias arrangeAllGrid: arrangeAllGridAction property alias arrangeSelection: arrangeSelectionAction property alias arrangeSelectionLock: arrangeSelectionLockAction property alias resetAllTranslation: resetAllTranslationAction @@ -464,8 +464,8 @@ Item Action { - id: arrangeAllLockAction - text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models Without Rotation") + id: arrangeAllGridAction + text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models in a grid") onTriggered: Printer.arrangeAll(true) shortcut: "Shift+Ctrl+R" } diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index f07b4d9571..75d366e5db 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -66,7 +66,7 @@ Cura.Menu Cura.MenuSeparator {} Cura.MenuItem { action: Cura.Actions.selectAll } Cura.MenuItem { action: Cura.Actions.arrangeAll } - Cura.MenuItem { action: Cura.Actions.arrangeAllLock } + Cura.MenuItem { action: Cura.Actions.arrangeAllGrid } Cura.MenuItem { action: Cura.Actions.deleteAll } Cura.MenuItem { action: Cura.Actions.reloadAll } Cura.MenuItem { action: Cura.Actions.resetAllTranslation } @@ -110,7 +110,7 @@ Cura.Menu minimumWidth: UM.Theme.getSize("small_popup_dialog").width minimumHeight: UM.Theme.getSize("small_popup_dialog").height - onAccepted: CuraActions.multiplySelection(copiesField.value, lockRotationField.checked) + onAccepted: CuraActions.multiplySelection(copiesField.value, gridPlacementSelected.checked) buttonSpacing: UM.Theme.getSize("thin_margin").width @@ -158,8 +158,8 @@ Cura.Menu UM.CheckBox { - id: lockRotationField - text: catalog.i18nc("@label", "Lock Rotation") + id: gridPlacementSelected + text: catalog.i18nc("@label", "Grid Placement") } } } From 2089462cd8121fe0e220e38c9909a13f688d8b9b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 22 Aug 2023 11:16:02 +0200 Subject: [PATCH 08/30] place objects outside grid Co-authored-by: Casper Lamboo CURA-7951 --- cura/Arranging/GridArrange.py | 60 +++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 0d76ecff52..212f7b8e51 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -26,13 +26,6 @@ class GridArrange: self._build_volume_bounding_box = build_volume.getBoundingBox() self._fixed_nodes = fixed_nodes - def arrange(self)-> bool: - - grouped_operation, not_fit_count = self.createGroupOperationForArrange() - grouped_operation.push() - return not_fit_count == 0 - - def createGroupOperationForArrange(self) -> Tuple[GroupedOperation, int]: self._grid_width = 0 self._grid_height = 0 for node in self._nodes_to_arrange: @@ -40,6 +33,19 @@ class GridArrange: self._grid_width = max(self._grid_width, bounding_box.width) self._grid_height = max(self._grid_height, bounding_box.depth) + coord_initial_leftover_x = self._build_volume_bounding_box.right + 2 * self._grid_width + coord_initial_leftover_y = (self._build_volume_bounding_box.back + self._build_volume_bounding_box.front) * 0.5 + self._initial_leftover_grid_x, self._initial_leftover_grid_y = self.coordSpaceToGridSpace(coord_initial_leftover_x, coord_initial_leftover_y) + self._initial_leftover_grid_x = math.floor(self._initial_leftover_grid_x) + self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y) + + def arrange(self)-> bool: + + grouped_operation, not_fit_count = self.createGroupOperationForArrange() + grouped_operation.push() + return not_fit_count == 0 + + def createGroupOperationForArrange(self) -> Tuple[GroupedOperation, int]: # Find grid indexes that intersect with fixed objects fixed_nodes_grid_ids = set() for node in self._fixed_nodes: @@ -67,21 +73,41 @@ class GridArrange: grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) grid_x, grid_y = grid_id - coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) - center_grid_x = coord_grid_x+(0.5 * self._grid_width) - center_grid_y = coord_grid_y+(0.5 * self._grid_height) + operation = self.moveNodeOnGrid(node, grid_x, grid_y) + grouped_operation.addOperation(operation) - bounding_box = node.getBoundingBox() - center_node_x = (bounding_box.left + bounding_box.right) * 0.5 - center_node_y = (bounding_box.back + bounding_box.front) * 0.5 + leftover_nodes = self._nodes_to_arrange[len(sequence):] - delta_x = center_grid_x - center_node_x - delta_y = center_grid_y - center_node_y - grouped_operation.addOperation(TranslateOperation(node, Vector(delta_x, 0, delta_y))) + left_over_grid_y = self._initial_leftover_grid_y + for node in leftover_nodes: + grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) + + # find the first next grid position that isn't occupied by a fixed node + while (self._initial_leftover_grid_x, left_over_grid_y) in fixed_nodes_grid_ids: + left_over_grid_y = left_over_grid_y - 1 + + operation = self.moveNodeOnGrid(node, self._initial_leftover_grid_x, left_over_grid_y) + grouped_operation.addOperation(operation) + + left_over_grid_y = left_over_grid_y - 1 self.drawDebugSvg() - return grouped_operation, 0 + return grouped_operation, len(leftover_nodes) + + def moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation": + coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) + center_grid_x = coord_grid_x + (0.5 * self._grid_width) + center_grid_y = coord_grid_y + (0.5 * self._grid_height) + + bounding_box = node.getBoundingBox() + center_node_x = (bounding_box.left + bounding_box.right) * 0.5 + center_node_y = (bounding_box.back + bounding_box.front) * 0.5 + + delta_x = center_grid_x - center_node_x + delta_y = center_grid_y - center_node_y + + return TranslateOperation(node, Vector(delta_x, 0, delta_y)) def getGridCornerPoints(self, bounding_box: "BoundingVolume") -> Tuple[float, float, float, float]: coord_x1 = bounding_box.left From 7449e2137c979b49ccf9a4f423344ed887070bb9 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 22 Aug 2023 11:26:43 +0200 Subject: [PATCH 09/30] objects placed with an offset to the grid Co-authored-by: Casper Lamboo CURA-7951 --- cura/Arranging/GridArrange.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 212f7b8e51..fbfaa7f40e 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -97,8 +97,8 @@ class GridArrange: def moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation": coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) - center_grid_x = coord_grid_x + (0.5 * self._grid_width) - center_grid_y = coord_grid_y + (0.5 * self._grid_height) + center_grid_x = coord_grid_x + (0.5 * (self._grid_width + self.offset_x)) + center_grid_y = coord_grid_y + (0.5 * (self._grid_height + self.offset_y)) bounding_box = node.getBoundingBox() center_node_x = (bounding_box.left + bounding_box.right) * 0.5 From b662da732eeb5c310db4ab07e748624e12441c1d Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 22 Aug 2023 11:51:30 +0200 Subject: [PATCH 10/30] review comments fixed Co-authored-by: Casper Lamboo CURA-7951 --- cura/Arranging/GridArrange.py | 6 ------ cura/CuraActions.py | 15 +++++++++++---- cura/CuraApplication.py | 11 +++++++++-- resources/qml/Actions.qml | 4 ++-- resources/qml/Menus/ContextMenu.qml | 11 ++++++++++- 5 files changed, 32 insertions(+), 15 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index fbfaa7f40e..6d4db360f7 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -21,7 +21,6 @@ class GridArrange: _build_volume_bounding_box = AxisAlignedBox def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = []): - print("len(nodes_to_arrange)", len(nodes_to_arrange)) self._nodes_to_arrange = nodes_to_arrange self._build_volume_bounding_box = build_volume.getBoundingBox() self._fixed_nodes = fixed_nodes @@ -72,7 +71,6 @@ class GridArrange: for grid_id, node in zip(sequence, self._nodes_to_arrange): grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) grid_x, grid_y = grid_id - operation = self.moveNodeOnGrid(node, grid_x, grid_y) grouped_operation.addOperation(operation) @@ -81,18 +79,14 @@ class GridArrange: left_over_grid_y = self._initial_leftover_grid_y for node in leftover_nodes: grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) - # find the first next grid position that isn't occupied by a fixed node while (self._initial_leftover_grid_x, left_over_grid_y) in fixed_nodes_grid_ids: left_over_grid_y = left_over_grid_y - 1 operation = self.moveNodeOnGrid(node, self._initial_leftover_grid_x, left_over_grid_y) grouped_operation.addOperation(operation) - left_over_grid_y = left_over_grid_y - 1 - self.drawDebugSvg() - return grouped_operation, len(leftover_nodes) def moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation": diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 1b98bdddd8..bd5787de42 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -83,18 +83,25 @@ class CuraActions(QObject): center_operation = TranslateOperation(current_node, Vector(0, center_y, 0), set_position=True) operation.addOperation(center_operation) operation.push() + @pyqtSlot(int) + def multiplySelection(self, count: int) -> None: + """Multiply all objects in the selection + :param count: The number of times to multiply the selection. + """ + min_offset = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors + job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset = max(min_offset, 8)) + job.start() - @pyqtSlot(int, bool) - def multiplySelection(self, count: int, grid_placement: bool) -> None: + @pyqtSlot(int) + def multiplySelectionToGrid(self, count: int) -> None: """Multiply all objects in the selection :param count: The number of times to multiply the selection. - :param grid_placement: If set to true objects are placed in a grid """ min_offset = cura.CuraApplication.CuraApplication.getInstance().getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors job = MultiplyObjectsJob(Selection.getAllSelectedObjects(), count, min_offset=max(min_offset, 8), - grid_arrange=grid_placement) + grid_arrange=True) job.start() @pyqtSlot() diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index c6c0ed3cd2..a9517c966c 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1440,8 +1440,15 @@ class CuraApplication(QtApplication): op.push() # Single build plate - @pyqtSlot(bool) - def arrangeAll(self, grid_arrangement: bool) -> None: + @pyqtSlot() + def arrangeAll(self) -> None: + self._arrangeAll(False) + + @pyqtSlot() + def arrangeAllInGrid(self) -> None: + self._arrangeAll(True) + + def _arrangeAll(self, grid_arrangement: bool) -> None: nodes_to_arrange = [] active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate locked_nodes = [] diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 1c84ec66b7..0701ba48ce 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -458,7 +458,7 @@ Item { id: arrangeAllAction text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models") - onTriggered: Printer.arrangeAll(false) + onTriggered: Printer.arrangeAll() shortcut: "Ctrl+R" } @@ -466,7 +466,7 @@ Item { id: arrangeAllGridAction text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models in a grid") - onTriggered: Printer.arrangeAll(true) + onTriggered: Printer.arrangeAllInGrid() shortcut: "Shift+Ctrl+R" } diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index 75d366e5db..a095fb6e1a 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -110,7 +110,16 @@ Cura.Menu minimumWidth: UM.Theme.getSize("small_popup_dialog").width minimumHeight: UM.Theme.getSize("small_popup_dialog").height - onAccepted: CuraActions.multiplySelection(copiesField.value, gridPlacementSelected.checked) + onAccepted: { + if (gridPlacementSelected.checked) + { + CuraActions.multiplySelectionToGrid(copiesField.value) + } + else + { + CuraActions.multiplySelection(copiesField.value) + } + } buttonSpacing: UM.Theme.getSize("thin_margin").width From 4096fc864b524db6d3da45526abfc08e6c9604d2 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 22 Aug 2023 15:06:17 +0200 Subject: [PATCH 11/30] grid placement available for elliptical buildplates Co-authored-by: Casper Lamboo CURA-7951 --- cura/Arranging/ArrangeObjectsJob.py | 2 +- cura/Arranging/GridArrange.py | 90 +++++++++++++++++++++++------ cura/BuildVolume.py | 3 + cura/CuraActions.py | 2 +- resources/qml/Actions.qml | 16 ----- 5 files changed, 78 insertions(+), 35 deletions(-) diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index d122098565..4bb55bf660 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -15,7 +15,7 @@ i18n_catalog = i18nCatalog("cura") class ArrangeObjectsJob(Job): - def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset=8, + def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8, grid_arrange: bool = False) -> None: super().__init__() self._nodes = nodes diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 6d4db360f7..5280843bd3 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -18,10 +18,12 @@ class GridArrange: _nodes_to_arrange: List["SceneNode"] _fixed_nodes: List["SceneNode"] - _build_volume_bounding_box = AxisAlignedBox + _build_volume: "BuildVolume" + _build_volume_bounding_box: AxisAlignedBox def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = []): self._nodes_to_arrange = nodes_to_arrange + self._build_volume = build_volume self._build_volume_bounding_box = build_volume.getBoundingBox() self._fixed_nodes = fixed_nodes @@ -39,7 +41,6 @@ class GridArrange: self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y) def arrange(self)-> bool: - grouped_operation, not_fit_count = self.createGroupOperationForArrange() grouped_operation.push() return not_fit_count == 0 @@ -51,6 +52,11 @@ class GridArrange: fixed_nodes_grid_ids = fixed_nodes_grid_ids.union(self.intersectingGridIdxInclusive(node.getBoundingBox())) build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) + + # Filter out the corner grid squares if the build plate shape is elliptic + if self._build_volume.getShape() == "elliptic": + build_plate_grid_ids = set(filter(lambda grid_id: self.checkGridUnderDiscSpace(grid_id[0], grid_id[1]), build_plate_grid_ids)) + allowed_grid_idx = build_plate_grid_ids.difference(fixed_nodes_grid_ids) # Find the sequence in which items are placed @@ -86,7 +92,6 @@ class GridArrange: operation = self.moveNodeOnGrid(node, self._initial_leftover_grid_x, left_over_grid_y) grouped_operation.addOperation(operation) left_over_grid_y = left_over_grid_y - 1 - return grouped_operation, len(leftover_nodes) def moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation": @@ -138,6 +143,35 @@ class GridArrange: coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self.offset_y) return coord_x, coord_y + def checkGridUnderDiscSpace(self, grid_x: int, grid_y: int) -> bool: + left, back = self.gridSpaceToCoordSpace(grid_x, grid_y) + right, front = self.gridSpaceToCoordSpace(grid_x + 1, grid_y + 1) + corners = [(left, back), (right, back), (right, front), (left, front)] + return all([self.checkPointUnderDiscSpace(x, y) for x, y in corners]) + + def checkPointUnderDiscSpace(self, x: float, y: float) -> bool: + disc_x, disc_y = self.coordSpaceToDiscSpace(x, y) + distance_to_center_squared = disc_x ** 2 + disc_y ** 2 + return distance_to_center_squared <= 1.0 + + def coordSpaceToDiscSpace(self, x: float, y: float) -> Tuple[float, float]: + # Transform coordinate system to + # + # coord_build_plate_left = -1 + # | coord_build_plate_right = 1 + # v (0,1) v + # ┌───────┬───────┐ < coord_build_plate_back = -1 + # │ │ │ + # │ │(0,0) │ + # (-1,0)│───────o───────┤(1,0) + # │ │ │ + # │ │ │ + # └───────┴───────┘ < coord_build_plate_front = +1 + # (0,-1) + disc_x = ((x - self._build_volume_bounding_box.left) / self._build_volume_bounding_box.width) * 2.0 - 1.0 + disc_y = ((y - self._build_volume_bounding_box.back) / self._build_volume_bounding_box.depth) * 2.0 - 1.0 + return disc_x, disc_y + def drawDebugSvg(self): with open("Builvolume_test.svg", "w") as f: build_volume_bounding_box = self._build_volume_bounding_box @@ -145,25 +179,39 @@ class GridArrange: f.write( f"\n") - f.write( - f""" - - """) + ellipse = True + if ellipse: + f.write( + f""" + + """) + else: + f.write( + f""" + + """) - for grid_x in range(-10, 10): - for grid_y in range(-10, 10): + for grid_x in range(0, 100): + for grid_y in range(0, 100): # if (grid_x, grid_y) in intersecting_grid_idx: # fill_color = "red" # elif (grid_x, grid_y) in build_plate_grid_idx: # fill_color = "green" # else: # fill_color = "orange" + coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) f.write( f""" @@ -172,12 +220,13 @@ class GridArrange: y="{coord_grid_y}" width="{self._grid_width}" height="{self._grid_height}" - fill="green" + fill="#ff00ff88" stroke="black" /> """) f.write(f""" @@ -208,4 +257,11 @@ class GridArrange: stroke-width="3" /> """) - f.write(f"") + + for x in range(math.floor(self._build_volume_bounding_box.left), math.floor(self._build_volume_bounding_box.right), 50): + for y in range(math.floor(self._build_volume_bounding_box.back), math.floor(self._build_volume_bounding_box.front), 50): + color = "green" if self.checkPointUnderDiscSpace(x, y) else "red" + f.write(f""" + + """) + f.write(f"") \ No newline at end of file diff --git a/cura/BuildVolume.py b/cura/BuildVolume.py index 0d6ecf5810..045156dcce 100755 --- a/cura/BuildVolume.py +++ b/cura/BuildVolume.py @@ -203,6 +203,9 @@ class BuildVolume(SceneNode): if shape: self._shape = shape + def getShape(self) -> str: + return self._shape + def getDiagonalSize(self) -> float: """Get the length of the 3D diagonal through the build volume. diff --git a/cura/CuraActions.py b/cura/CuraActions.py index bd5787de42..6e6e93e3ad 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -80,7 +80,7 @@ class CuraActions(QObject): center_y = 0 # Move the object so that it's bottom is on to of the buildplate - center_operation = TranslateOperation(current_node, Vector(0, center_y, 0), set_position=True) + center_operation = TranslateOperation(current_node, Vector(0, center_y, 0), set_position = True) operation.addOperation(center_operation) operation.push() @pyqtSlot(int) diff --git a/resources/qml/Actions.qml b/resources/qml/Actions.qml index 0701ba48ce..65888b3493 100644 --- a/resources/qml/Actions.qml +++ b/resources/qml/Actions.qml @@ -42,8 +42,6 @@ Item property alias reloadAll: reloadAllAction property alias arrangeAll: arrangeAllAction property alias arrangeAllGrid: arrangeAllGridAction - property alias arrangeSelection: arrangeSelectionAction - property alias arrangeSelectionLock: arrangeSelectionLockAction property alias resetAllTranslation: resetAllTranslationAction property alias resetAll: resetAllAction @@ -470,20 +468,6 @@ Item shortcut: "Shift+Ctrl+R" } - Action - { - id: arrangeSelectionAction - text: catalog.i18nc("@action:inmenu menubar:edit","Arrange Selection") - onTriggered: Printer.arrangeSelection(false) - } - - Action - { - id: arrangeSelectionLockAction - text: catalog.i18nc("@action:inmenu menubar:edit","Arrange Selection Without Rotation") - onTriggered: Printer.arrangeSelection(true) - } - Action { id: resetAllTranslationAction From 118f49a0520bdd1a3983bbf33b49c3ed8a645fb1 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Wed, 23 Aug 2023 15:39:23 +0200 Subject: [PATCH 12/30] review comments fixed Co-authored-by: Casper Lamboo CURA-7951 --- cura/Arranging/ArrangeObjectsJob.py | 10 ++---- cura/Arranging/GridArrange.py | 39 +++++++++----------- cura/Arranging/Nest2DArrange.py | 56 ++++++++++++++++------------- cura/CuraActions.py | 4 +-- cura/CuraApplication.py | 13 ++++--- cura/MultiplyObjectsJob.py | 21 +++++------ resources/qml/Menus/ContextMenu.qml | 23 ++++++------ 7 files changed, 76 insertions(+), 90 deletions(-) diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index 4bb55bf660..0f8c576995 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -16,7 +16,7 @@ i18n_catalog = i18nCatalog("cura") class ArrangeObjectsJob(Job): def __init__(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], min_offset = 8, - grid_arrange: bool = False) -> None: + *, grid_arrange: bool = False) -> None: super().__init__() self._nodes = nodes self._fixed_nodes = fixed_nodes @@ -33,13 +33,7 @@ class ArrangeObjectsJob(Job): status_message.show() try: - - if self._grid_arrange: - grid_arrange = GridArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) - found_solution_for_all = grid_arrange.arrange() - - else: - found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, grid_arrange= self._grid_arrange) except: # If the thread crashes, the message should still close Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 5280843bd3..d8b7ba9db8 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -1,6 +1,11 @@ import math from typing import List, TYPE_CHECKING, Optional, Tuple, Set + + +if TYPE_CHECKING: + from UM.Scene.SceneNode import SceneNode + from UM.Application import Application from UM.Math import AxisAlignedBox from UM.Math.Vector import Vector @@ -10,23 +15,16 @@ from UM.Operations.TranslateOperation import TranslateOperation class GridArrange: - offset_x: float = 10 - offset_y: float = 10 - - _grid_width: float - _grid_height: float - - _nodes_to_arrange: List["SceneNode"] - _fixed_nodes: List["SceneNode"] - _build_volume: "BuildVolume" - _build_volume_bounding_box: AxisAlignedBox - - def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = []): + def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = None): + if fixed_nodes is None: + fixed_nodes = [] self._nodes_to_arrange = nodes_to_arrange self._build_volume = build_volume self._build_volume_bounding_box = build_volume.getBoundingBox() self._fixed_nodes = fixed_nodes + self._offset_x: float = 10 + self._offset_y: float = 10 self._grid_width = 0 self._grid_height = 0 for node in self._nodes_to_arrange: @@ -40,11 +38,6 @@ class GridArrange: self._initial_leftover_grid_x = math.floor(self._initial_leftover_grid_x) self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y) - def arrange(self)-> bool: - grouped_operation, not_fit_count = self.createGroupOperationForArrange() - grouped_operation.push() - return not_fit_count == 0 - def createGroupOperationForArrange(self) -> Tuple[GroupedOperation, int]: # Find grid indexes that intersect with fixed objects fixed_nodes_grid_ids = set() @@ -96,8 +89,8 @@ class GridArrange: def moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation": coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) - center_grid_x = coord_grid_x + (0.5 * (self._grid_width + self.offset_x)) - center_grid_y = coord_grid_y + (0.5 * (self._grid_height + self.offset_y)) + center_grid_x = coord_grid_x + (0.5 * (self._grid_width + self._offset_x)) + center_grid_y = coord_grid_y + (0.5 * (self._grid_height + self._offset_y)) bounding_box = node.getBoundingBox() center_node_x = (bounding_box.left + bounding_box.right) * 0.5 @@ -134,13 +127,13 @@ class GridArrange: return grid_idx def gridSpaceToCoordSpace(self, x: float, y: float) -> Tuple[float, float]: - grid_x = x * (self._grid_width + self.offset_x) + self._build_volume_bounding_box.left - grid_y = y * (self._grid_height + self.offset_y) + self._build_volume_bounding_box.back + grid_x = x * (self._grid_width + self._offset_x) + self._build_volume_bounding_box.left + grid_y = y * (self._grid_height + self._offset_y) + self._build_volume_bounding_box.back return grid_x, grid_y def coordSpaceToGridSpace(self, grid_x: float, grid_y: float) -> Tuple[float, float]: - coord_x = (grid_x - self._build_volume_bounding_box.left) / (self._grid_width + self.offset_x) - coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self.offset_y) + coord_x = (grid_x - self._build_volume_bounding_box.left) / (self._grid_width + self._offset_x) + coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self._offset_y) return coord_x, coord_y def checkGridUnderDiscSpace(self, grid_x: int, grid_y: int) -> bool: diff --git a/cura/Arranging/Nest2DArrange.py b/cura/Arranging/Nest2DArrange.py index 8921c9ede2..b7422e3943 100644 --- a/cura/Arranging/Nest2DArrange.py +++ b/cura/Arranging/Nest2DArrange.py @@ -15,7 +15,7 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.RotateOperation import RotateOperation from UM.Operations.TranslateOperation import TranslateOperation - +from cura.Arranging.GridArrange import GridArrange if TYPE_CHECKING: from UM.Scene.SceneNode import SceneNode @@ -27,6 +27,7 @@ def findNodePlacement( build_volume: "BuildVolume", fixed_nodes: Optional[List["SceneNode"]] = None, factor: int = 10000, + *, lock_rotation: bool = False ) -> Tuple[bool, List[Item]]: """ @@ -124,30 +125,36 @@ def createGroupOperationForArrange(nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: Optional[List["SceneNode"]] = None, factor: int = 10000, + *, add_new_nodes_in_scene: bool = False, - lock_rotation: bool = False) -> Tuple[GroupedOperation, int]: - scene_root = Application.getInstance().getController().getScene().getRoot() - found_solution_for_all, node_items = findNodePlacement(nodes_to_arrange, build_volume, fixed_nodes, factor, - lock_rotation) + lock_rotation: bool = False, + grid_arrange: bool = False) -> Tuple[GroupedOperation, int]: + if grid_arrange: + grid = GridArrange(nodes_to_arrange, build_volume, fixed_nodes) + return grid.createGroupOperationForArrange() + else: + scene_root = Application.getInstance().getController().getScene().getRoot() + found_solution_for_all, node_items = findNodePlacement(nodes_to_arrange, build_volume, fixed_nodes, factor, + lock_rotation = lock_rotation) - not_fit_count = 0 - grouped_operation = GroupedOperation() - for node, node_item in zip(nodes_to_arrange, node_items): - if add_new_nodes_in_scene: - grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) + not_fit_count = 0 + grouped_operation = GroupedOperation() + for node, node_item in zip(nodes_to_arrange, node_items): + if add_new_nodes_in_scene: + grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) - if node_item.binId() == 0: - # We found a spot for it - rotation_matrix = Matrix() - rotation_matrix.setByRotationAxis(node_item.rotation(), Vector(0, -1, 0)) - grouped_operation.addOperation(RotateOperation(node, Quaternion.fromMatrix(rotation_matrix))) - grouped_operation.addOperation(TranslateOperation(node, Vector(node_item.translation().x() / factor, 0, - node_item.translation().y() / factor))) - else: - # We didn't find a spot - grouped_operation.addOperation( - TranslateOperation(node, Vector(200, node.getWorldPosition().y, -not_fit_count * 20), set_position = True)) - not_fit_count += 1 + if node_item.binId() == 0: + # We found a spot for it + rotation_matrix = Matrix() + rotation_matrix.setByRotationAxis(node_item.rotation(), Vector(0, -1, 0)) + grouped_operation.addOperation(RotateOperation(node, Quaternion.fromMatrix(rotation_matrix))) + grouped_operation.addOperation(TranslateOperation(node, Vector(node_item.translation().x() / factor, 0, + node_item.translation().y() / factor))) + else: + # We didn't find a spot + grouped_operation.addOperation( + TranslateOperation(node, Vector(200, node.getWorldPosition().y, -not_fit_count * 20), set_position = True)) + not_fit_count += 1 return grouped_operation, not_fit_count @@ -158,7 +165,8 @@ def arrange( fixed_nodes: Optional[List["SceneNode"]] = None, factor=10000, add_new_nodes_in_scene: bool = False, - lock_rotation: bool = False + lock_rotation: bool = False, + grid_arrange: bool = False ) -> bool: """ Find placement for a set of scene nodes, and move them by using a single grouped operation. @@ -174,6 +182,6 @@ def arrange( """ grouped_operation, not_fit_count = createGroupOperationForArrange(nodes_to_arrange, build_volume, fixed_nodes, - factor, add_new_nodes_in_scene, lock_rotation) + factor, add_new_nodes_in_scene = add_new_nodes_in_scene, lock_rotation = lock_rotation, grid_arrange = grid_arrange) grouped_operation.push() return not_fit_count == 0 diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 6e6e93e3ad..dc18497191 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -18,7 +18,6 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation from UM.Operations.TranslateOperation import TranslateOperation import cura.CuraApplication -from cura.Arranging.GridArrange import GridArrange from cura.Operations.SetParentOperation import SetParentOperation from cura.MultiplyObjectsJob import MultiplyObjectsJob from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation @@ -239,8 +238,7 @@ class CuraActions(QObject): if node.callDecoration("isSliceable"): fixed_nodes.append(node) # Add the new nodes to the scene, and arrange them - grid_arrange = GridArrange(nodes, application.getBuildVolume(), fixed_nodes) - group_operation, not_fit_count = grid_arrange.createGroupOperationForArrange() + group_operation, not_fit_count = createGroupOperationForArrange(nodes, application.getBuildVolume(), fixed_nodes, grid_arrange = True) group_operation.push() # deselect currently selected nodes, and select the new nodes diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index a9517c966c..fb4b4cc5d6 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -1442,13 +1442,13 @@ class CuraApplication(QtApplication): # Single build plate @pyqtSlot() def arrangeAll(self) -> None: - self._arrangeAll(False) + self._arrangeAll(grid_arrangement = False) @pyqtSlot() def arrangeAllInGrid(self) -> None: - self._arrangeAll(True) + self._arrangeAll(grid_arrangement = True) - def _arrangeAll(self, grid_arrangement: bool) -> None: + def _arrangeAll(self, *, grid_arrangement: bool) -> None: nodes_to_arrange = [] active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate locked_nodes = [] @@ -1478,18 +1478,17 @@ class CuraApplication(QtApplication): locked_nodes.append(node) else: nodes_to_arrange.append(node) - self.arrange(nodes_to_arrange, locked_nodes, grid_arrangement) + self.arrange(nodes_to_arrange, locked_nodes, grid_arrangement = grid_arrangement) - def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], grid_arrangement: bool = False) -> None: + def arrange(self, nodes: List[SceneNode], fixed_nodes: List[SceneNode], *, grid_arrangement: bool = False) -> None: """Arrange a set of nodes given a set of fixed nodes :param nodes: nodes that we have to place :param fixed_nodes: nodes that are placed in the arranger before finding spots for nodes :param grid_arrangement: If set to true if objects are to be placed in a grid """ - min_offset = self.getBuildVolume().getEdgeDisallowedSize() + 2 # Allow for some rounding errors - job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset=max(min_offset, 8), grid_arrange =grid_arrangement) + job = ArrangeObjectsJob(nodes, fixed_nodes, min_offset = max(min_offset, 8), grid_arrange = grid_arrangement) job.start() @pyqtSlot() diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 6df80eb01c..3864b94427 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -14,14 +14,13 @@ from UM.Operations.TranslateOperation import TranslateOperation from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog -from cura.Arranging.GridArrange import GridArrange -from cura.Arranging.Nest2DArrange import arrange, createGroupOperationForArrange +from cura.Arranging.Nest2DArrange import createGroupOperationForArrange i18n_catalog = i18nCatalog("cura") class MultiplyObjectsJob(Job): - def __init__(self, objects, count: int, min_offset: int = 8, grid_arrange: bool = False): + def __init__(self, objects, count: int, min_offset: int = 8 ,* , grid_arrange: bool = False): super().__init__() self._objects = objects self._count: int = count @@ -78,16 +77,12 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True group_operation = GroupedOperation() if nodes: - if(self._grid_arrange): - grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(),fixed_nodes) - group_operation, not_fit_count = grid_arrange.createGroupOperationForArrange() - - else: - group_operation, not_fit_count = createGroupOperationForArrange(nodes, - Application.getInstance().getBuildVolume(), - fixed_nodes, - factor=10000, - add_new_nodes_in_scene=True) + group_operation, not_fit_count = createGroupOperationForArrange(nodes, + Application.getInstance().getBuildVolume(), + fixed_nodes, + factor=10000, + add_new_nodes_in_scene=True, + grid_arrange=self._grid_arrange) if nodes_to_add_without_arrange: for nested_node in nodes_to_add_without_arrange: diff --git a/resources/qml/Menus/ContextMenu.qml b/resources/qml/Menus/ContextMenu.qml index a095fb6e1a..2de2795a74 100644 --- a/resources/qml/Menus/ContextMenu.qml +++ b/resources/qml/Menus/ContextMenu.qml @@ -109,18 +109,7 @@ Cura.Menu height: UM.Theme.getSize("small_popup_dialog").height minimumWidth: UM.Theme.getSize("small_popup_dialog").width minimumHeight: UM.Theme.getSize("small_popup_dialog").height - - onAccepted: { - if (gridPlacementSelected.checked) - { - CuraActions.multiplySelectionToGrid(copiesField.value) - } - else - { - CuraActions.multiplySelection(copiesField.value) - } - } - + onAccepted: gridPlacementSelected.checked? CuraActions.multiplySelectionToGrid(copiesField.value) : CuraActions.multiplySelection(copiesField.value) buttonSpacing: UM.Theme.getSize("thin_margin").width rightButtons: @@ -169,7 +158,17 @@ Cura.Menu { id: gridPlacementSelected text: catalog.i18nc("@label", "Grid Placement") + + UM.ToolTip + { + visible: parent.hovered + targetPoint: Qt.point(parent.x + Math.round(parent.width / 2), parent.y) + x: 0 + y: parent.y + parent.height + UM.Theme.getSize("default_margin").height + tooltipText: catalog.i18nc("@info", "Multiply selected item and place them in a grid of build plate.") + } } + } } } From 668038c59f420843b166690486c698d79c957da6 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 23 Aug 2023 17:58:00 +0200 Subject: [PATCH 13/30] Implement factory for Arrange CURA-7951 --- cura/Arranging/ArrangeObjectsJob.py | 10 +- cura/Arranging/Arranger.py | 27 ++++ cura/Arranging/GridArrange.py | 12 +- cura/Arranging/Nest2DArrange.py | 228 ++++++++++++---------------- cura/CuraActions.py | 9 +- cura/CuraApplication.py | 1 - cura/MultiplyObjectsJob.py | 15 +- 7 files changed, 152 insertions(+), 150 deletions(-) create mode 100644 cura/Arranging/Arranger.py diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index 0f8c576995..b1e0432786 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -9,7 +9,7 @@ from UM.Message import Message from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog from cura.Arranging.GridArrange import GridArrange -from cura.Arranging.Nest2DArrange import arrange +from cura.Arranging.Nest2DArrange import Nest2DArrange i18n_catalog = i18nCatalog("cura") @@ -33,7 +33,13 @@ class ArrangeObjectsJob(Job): status_message.show() try: - found_solution_for_all = arrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, grid_arrange= self._grid_arrange) + if self._grid_arrange: + arranger = GridArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + else: + arranger = Nest2DArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, + factor=1000) + + found_solution_for_all = arranger.arrange() except: # If the thread crashes, the message should still close Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") diff --git a/cura/Arranging/Arranger.py b/cura/Arranging/Arranger.py new file mode 100644 index 0000000000..684fa1258b --- /dev/null +++ b/cura/Arranging/Arranger.py @@ -0,0 +1,27 @@ +from typing import List, TYPE_CHECKING, Optional, Tuple, Set + +if TYPE_CHECKING: + from UM.Operations.GroupedOperation import GroupedOperation + + +class Arranger: + def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple["GroupedOperation", int]: + """ + Find placement for a set of scene nodes, but don't actually move them just yet. + :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations + :return: tuple (found_solution_for_all, node_items) + WHERE + found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects + node_items: A list of the nodes return by libnest2d, which contain the new positions on the buildplate + """ + raise NotImplementedError + + def arrange(self, add_new_nodes_in_scene: bool = True) -> bool: + """ + Find placement for a set of scene nodes, and move them by using a single grouped operation. + :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations + :return: found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects + """ + grouped_operation, not_fit_count = self.createGroupOperationForArrange(add_new_nodes_in_scene) + grouped_operation.push() + return not_fit_count == 0 diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index d8b7ba9db8..4866e99b69 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -1,8 +1,6 @@ import math from typing import List, TYPE_CHECKING, Optional, Tuple, Set - - if TYPE_CHECKING: from UM.Scene.SceneNode import SceneNode @@ -12,9 +10,10 @@ from UM.Math.Vector import Vector from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.TranslateOperation import TranslateOperation +from cura.Arranging.Arranger import Arranger -class GridArrange: +class GridArrange(Arranger): def __init__(self, nodes_to_arrange: List["SceneNode"], build_volume: "BuildVolume", fixed_nodes: List["SceneNode"] = None): if fixed_nodes is None: fixed_nodes = [] @@ -38,7 +37,7 @@ class GridArrange: self._initial_leftover_grid_x = math.floor(self._initial_leftover_grid_x) self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y) - def createGroupOperationForArrange(self) -> Tuple[GroupedOperation, int]: + def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple[GroupedOperation, int]: # Find grid indexes that intersect with fixed objects fixed_nodes_grid_ids = set() for node in self._fixed_nodes: @@ -77,7 +76,8 @@ class GridArrange: left_over_grid_y = self._initial_leftover_grid_y for node in leftover_nodes: - grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) + if add_new_nodes_in_scene: + grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) # find the first next grid position that isn't occupied by a fixed node while (self._initial_leftover_grid_x, left_over_grid_y) in fixed_nodes_grid_ids: left_over_grid_y = left_over_grid_y - 1 @@ -156,7 +156,7 @@ class GridArrange: # ┌───────┬───────┐ < coord_build_plate_back = -1 # │ │ │ # │ │(0,0) │ - # (-1,0)│───────o───────┤(1,0) + # (-1,0)├───────o───────┤(1,0) # │ │ │ # │ │ │ # └───────┴───────┘ < coord_build_plate_front = +1 diff --git a/cura/Arranging/Nest2DArrange.py b/cura/Arranging/Nest2DArrange.py index b7422e3943..ea45fe0b8e 100644 --- a/cura/Arranging/Nest2DArrange.py +++ b/cura/Arranging/Nest2DArrange.py @@ -15,131 +15,122 @@ from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation from UM.Operations.RotateOperation import RotateOperation from UM.Operations.TranslateOperation import TranslateOperation -from cura.Arranging.GridArrange import GridArrange +from cura.Arranging.Arranger import Arranger if TYPE_CHECKING: from UM.Scene.SceneNode import SceneNode from cura.BuildVolume import BuildVolume -def findNodePlacement( - nodes_to_arrange: List["SceneNode"], - build_volume: "BuildVolume", - fixed_nodes: Optional[List["SceneNode"]] = None, - factor: int = 10000, - *, - lock_rotation: bool = False -) -> Tuple[bool, List[Item]]: - """ - Find placement for a set of scene nodes, but don't actually move them just yet. - :param nodes_to_arrange: The list of nodes that need to be moved. - :param build_volume: The build volume that we want to place the nodes in. It gets size & disallowed areas from this. - :param fixed_nodes: List of nods that should not be moved, but should be used when deciding where the others nodes - are placed. - :param factor: The library that we use is int based. This factor defines how accurate we want it to be. - :param lock_rotation: If set to true the orientation of the object will remain the same +class Nest2DArrange(Arranger): + def __init__(self, + nodes_to_arrange: List["SceneNode"], + build_volume: "BuildVolume", + fixed_nodes: Optional[List["SceneNode"]] = None, + *, + factor: int = 10000, + lock_rotation: bool = False): + """ + :param nodes_to_arrange: The list of nodes that need to be moved. + :param build_volume: The build volume that we want to place the nodes in. It gets size & disallowed areas from this. + :param fixed_nodes: List of nods that should not be moved, but should be used when deciding where the others nodes + are placed. + :param factor: The library that we use is int based. This factor defines how accuracte we want it to be. + :param lock_rotation: If set to true the orientation of the object will remain the same + """ + super().__init__() + self._nodes_to_arrange = nodes_to_arrange + self._build_volume = build_volume + self._fixed_nodes = fixed_nodes + self._factor = factor + self._lock_rotation = lock_rotation - :return: tuple (found_solution_for_all, node_items) - WHERE - found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects - node_items: A list of the nodes return by libnest2d, which contain the new positions on the buildplate - """ - spacing = int(1.5 * factor) # 1.5mm spacing. + def findNodePlacement(self) -> Tuple[bool, List[Item]]: + spacing = int(1.5 * self._factor) # 1.5mm spacing. - machine_width = build_volume.getWidth() - machine_depth = build_volume.getDepth() - build_plate_bounding_box = Box(int(machine_width * factor), int(machine_depth * factor)) + machine_width = self._build_volume.getWidth() + machine_depth = self._build_volume.getDepth() + build_plate_bounding_box = Box(int(machine_width * self._factor), int(machine_depth * self._factor)) - if fixed_nodes is None: - fixed_nodes = [] + if self._fixed_nodes is None: + self._fixed_nodes = [] - # Add all the items we want to arrange - node_items = [] - for node in nodes_to_arrange: - hull_polygon = node.callDecoration("getConvexHull") - if not hull_polygon or hull_polygon.getPoints is None: - Logger.log("w", "Object {} cannot be arranged because it has no convex hull.".format(node.getName())) - continue - converted_points = [] - for point in hull_polygon.getPoints(): - converted_points.append(Point(int(point[0] * factor), int(point[1] * factor))) - item = Item(converted_points) - node_items.append(item) - - # Use a tiny margin for the build_plate_polygon (the nesting doesn't like overlapping disallowed areas) - half_machine_width = 0.5 * machine_width - 1 - half_machine_depth = 0.5 * machine_depth - 1 - build_plate_polygon = Polygon(numpy.array([ - [half_machine_width, -half_machine_depth], - [-half_machine_width, -half_machine_depth], - [-half_machine_width, half_machine_depth], - [half_machine_width, half_machine_depth] - ], numpy.float32)) - - disallowed_areas = build_volume.getDisallowedAreas() - num_disallowed_areas_added = 0 - for area in disallowed_areas: - converted_points = [] - - # Clip the disallowed areas so that they don't overlap the bounding box (The arranger chokes otherwise) - clipped_area = area.intersectionConvexHulls(build_plate_polygon) - - if clipped_area.getPoints() is not None and len(clipped_area.getPoints()) > 2: # numpy array has to be explicitly checked against None - for point in clipped_area.getPoints(): - converted_points.append(Point(int(point[0] * factor), int(point[1] * factor))) - - disallowed_area = Item(converted_points) - disallowed_area.markAsDisallowedAreaInBin(0) - node_items.append(disallowed_area) - num_disallowed_areas_added += 1 - - for node in fixed_nodes: - converted_points = [] - hull_polygon = node.callDecoration("getConvexHull") - - if hull_polygon is not None and hull_polygon.getPoints() is not None and len(hull_polygon.getPoints()) > 2: # numpy array has to be explicitly checked against None + # Add all the items we want to arrange + node_items = [] + for node in self._nodes_to_arrange: + hull_polygon = node.callDecoration("getConvexHull") + if not hull_polygon or hull_polygon.getPoints is None: + Logger.log("w", "Object {} cannot be arranged because it has no convex hull.".format(node.getName())) + continue + converted_points = [] for point in hull_polygon.getPoints(): - converted_points.append(Point(int(point[0] * factor), int(point[1] * factor))) + converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor))) item = Item(converted_points) - item.markAsFixedInBin(0) node_items.append(item) - num_disallowed_areas_added += 1 - config = NfpConfig() - config.accuracy = 1.0 - config.alignment = NfpConfig.Alignment.DONT_ALIGN - if lock_rotation: - config.rotations = [0.0] + # Use a tiny margin for the build_plate_polygon (the nesting doesn't like overlapping disallowed areas) + half_machine_width = 0.5 * machine_width - 1 + half_machine_depth = 0.5 * machine_depth - 1 + build_plate_polygon = Polygon(numpy.array([ + [half_machine_width, -half_machine_depth], + [-half_machine_width, -half_machine_depth], + [-half_machine_width, half_machine_depth], + [half_machine_width, half_machine_depth] + ], numpy.float32)) - num_bins = nest(node_items, build_plate_bounding_box, spacing, config) + disallowed_areas = self._build_volume.getDisallowedAreas() + num_disallowed_areas_added = 0 + for area in disallowed_areas: + converted_points = [] - # Strip the fixed items (previously placed) and the disallowed areas from the results again. - node_items = list(filter(lambda item: not item.isFixed(), node_items)) + # Clip the disallowed areas so that they don't overlap the bounding box (The arranger chokes otherwise) + clipped_area = area.intersectionConvexHulls(build_plate_polygon) - found_solution_for_all = num_bins == 1 + if clipped_area.getPoints() is not None and len( + clipped_area.getPoints()) > 2: # numpy array has to be explicitly checked against None + for point in clipped_area.getPoints(): + converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor))) - return found_solution_for_all, node_items + disallowed_area = Item(converted_points) + disallowed_area.markAsDisallowedAreaInBin(0) + node_items.append(disallowed_area) + num_disallowed_areas_added += 1 + for node in self._fixed_nodes: + converted_points = [] + hull_polygon = node.callDecoration("getConvexHull") -def createGroupOperationForArrange(nodes_to_arrange: List["SceneNode"], - build_volume: "BuildVolume", - fixed_nodes: Optional[List["SceneNode"]] = None, - factor: int = 10000, - *, - add_new_nodes_in_scene: bool = False, - lock_rotation: bool = False, - grid_arrange: bool = False) -> Tuple[GroupedOperation, int]: - if grid_arrange: - grid = GridArrange(nodes_to_arrange, build_volume, fixed_nodes) - return grid.createGroupOperationForArrange() - else: + if hull_polygon is not None and hull_polygon.getPoints() is not None and len( + hull_polygon.getPoints()) > 2: # numpy array has to be explicitly checked against None + for point in hull_polygon.getPoints(): + converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor))) + item = Item(converted_points) + item.markAsFixedInBin(0) + node_items.append(item) + num_disallowed_areas_added += 1 + + config = NfpConfig() + config.accuracy = 1.0 + config.alignment = NfpConfig.Alignment.DONT_ALIGN + if self._lock_rotation: + config.rotations = [0.0] + + num_bins = nest(node_items, build_plate_bounding_box, spacing, config) + + # Strip the fixed items (previously placed) and the disallowed areas from the results again. + node_items = list(filter(lambda item: not item.isFixed(), node_items)) + + found_solution_for_all = num_bins == 1 + + return found_solution_for_all, node_items + + def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple[GroupedOperation, int]: scene_root = Application.getInstance().getController().getScene().getRoot() - found_solution_for_all, node_items = findNodePlacement(nodes_to_arrange, build_volume, fixed_nodes, factor, - lock_rotation = lock_rotation) + found_solution_for_all, node_items = self.findNodePlacement() not_fit_count = 0 grouped_operation = GroupedOperation() - for node, node_item in zip(nodes_to_arrange, node_items): + for node, node_item in zip(self._nodes_to_arrange, node_items): if add_new_nodes_in_scene: grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) @@ -148,40 +139,13 @@ def createGroupOperationForArrange(nodes_to_arrange: List["SceneNode"], rotation_matrix = Matrix() rotation_matrix.setByRotationAxis(node_item.rotation(), Vector(0, -1, 0)) grouped_operation.addOperation(RotateOperation(node, Quaternion.fromMatrix(rotation_matrix))) - grouped_operation.addOperation(TranslateOperation(node, Vector(node_item.translation().x() / factor, 0, - node_item.translation().y() / factor))) + grouped_operation.addOperation( + TranslateOperation(node, Vector(node_item.translation().x() / self._factor, 0, + node_item.translation().y() / self._factor))) else: # We didn't find a spot grouped_operation.addOperation( TranslateOperation(node, Vector(200, node.getWorldPosition().y, -not_fit_count * 20), set_position = True)) not_fit_count += 1 - return grouped_operation, not_fit_count - - -def arrange( - nodes_to_arrange: List["SceneNode"], - build_volume: "BuildVolume", - fixed_nodes: Optional[List["SceneNode"]] = None, - factor=10000, - add_new_nodes_in_scene: bool = False, - lock_rotation: bool = False, - grid_arrange: bool = False -) -> bool: - """ - Find placement for a set of scene nodes, and move them by using a single grouped operation. - :param nodes_to_arrange: The list of nodes that need to be moved. - :param build_volume: The build volume that we want to place the nodes in. It gets size & disallowed areas from this. - :param fixed_nodes: List of nods that should not be moved, but should be used when deciding where the others nodes - are placed. - :param factor: The library that we use is int based. This factor defines how accuracte we want it to be. - :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations - :param lock_rotation: If set to true the orientation of the object will remain the same - - :return: found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects - """ - - grouped_operation, not_fit_count = createGroupOperationForArrange(nodes_to_arrange, build_volume, fixed_nodes, - factor, add_new_nodes_in_scene = add_new_nodes_in_scene, lock_rotation = lock_rotation, grid_arrange = grid_arrange) - grouped_operation.push() - return not_fit_count == 0 + return grouped_operation, not_fit_count diff --git a/cura/CuraActions.py b/cura/CuraActions.py index dc18497191..29f50d88b2 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -22,7 +22,10 @@ from cura.Operations.SetParentOperation import SetParentOperation from cura.MultiplyObjectsJob import MultiplyObjectsJob from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation from cura.Settings.ExtruderManager import ExtruderManager -from cura.Arranging.Nest2DArrange import createGroupOperationForArrange + +from cura.Arranging.GridArrange import GridArrange +from cura.Arranging.Nest2DArrange import Nest2DArrange + from cura.Operations.SetBuildPlateNumberOperation import SetBuildPlateNumberOperation @@ -238,7 +241,9 @@ class CuraActions(QObject): if node.callDecoration("isSliceable"): fixed_nodes.append(node) # Add the new nodes to the scene, and arrange them - group_operation, not_fit_count = createGroupOperationForArrange(nodes, application.getBuildVolume(), fixed_nodes, grid_arrange = True) + + arranger = GridArrange(nodes, application.getBuildVolume(), fixed_nodes) + group_operation, not_fit_count = arranger.createGroupOperationForArrange() group_operation.push() # deselect currently selected nodes, and select the new nodes diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index fb4b4cc5d6..54b717b3d0 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -54,7 +54,6 @@ from cura import ApplicationMetadata from cura.API import CuraAPI from cura.API.Account import Account from cura.Arranging.ArrangeObjectsJob import ArrangeObjectsJob -from cura.Arranging.Nest2DArrange import arrange from cura.Machines.MachineErrorChecker import MachineErrorChecker from cura.Machines.Models.BuildPlateModel import BuildPlateModel from cura.Machines.Models.CustomQualityProfilesDropDownMenuModel import CustomQualityProfilesDropDownMenuModel diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 3864b94427..889b6f5d1a 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -14,7 +14,8 @@ from UM.Operations.TranslateOperation import TranslateOperation from UM.Scene.Iterator.DepthFirstIterator import DepthFirstIterator from UM.Scene.SceneNode import SceneNode from UM.i18n import i18nCatalog -from cura.Arranging.Nest2DArrange import createGroupOperationForArrange +from cura.Arranging.GridArrange import GridArrange +from cura.Arranging.Nest2DArrange import Nest2DArrange i18n_catalog = i18nCatalog("cura") @@ -77,12 +78,12 @@ class MultiplyObjectsJob(Job): found_solution_for_all = True group_operation = GroupedOperation() if nodes: - group_operation, not_fit_count = createGroupOperationForArrange(nodes, - Application.getInstance().getBuildVolume(), - fixed_nodes, - factor=10000, - add_new_nodes_in_scene=True, - grid_arrange=self._grid_arrange) + if self._grid_arrange: + arranger = GridArrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes) + else: + arranger = Nest2DArrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes, factor=1000) + + group_operation, not_fit_count = arranger.createGroupOperationForArrange(add_new_nodes_in_scene=True) if nodes_to_add_without_arrange: for nested_node in nodes_to_add_without_arrange: From f67a6970ddd5a189f148fbf45377d56ba3af89e9 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 24 Aug 2023 08:33:59 +0200 Subject: [PATCH 14/30] Find optimal offset for grid arrange CURA-7951 --- cura/Arranging/GridArrange.py | 243 ++++++++++++++++++++++++---------- 1 file changed, 170 insertions(+), 73 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 4866e99b69..82ce7bc224 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -1,11 +1,11 @@ import math -from typing import List, TYPE_CHECKING, Optional, Tuple, Set +from typing import List, TYPE_CHECKING, Tuple, Set if TYPE_CHECKING: from UM.Scene.SceneNode import SceneNode + from cura.BuildVolume import BuildVolume from UM.Application import Application -from UM.Math import AxisAlignedBox from UM.Math.Vector import Vector from UM.Operations.AddSceneNodeOperation import AddSceneNodeOperation from UM.Operations.GroupedOperation import GroupedOperation @@ -22,14 +22,25 @@ class GridArrange(Arranger): self._build_volume_bounding_box = build_volume.getBoundingBox() self._fixed_nodes = fixed_nodes - self._offset_x: float = 10 - self._offset_y: float = 10 + self._margin_x: float = 1 + self._margin_y: float = 1 + self._grid_width = 0 self._grid_height = 0 for node in self._nodes_to_arrange: bounding_box = node.getBoundingBox() self._grid_width = max(self._grid_width, bounding_box.width) self._grid_height = max(self._grid_height, bounding_box.depth) + self._grid_width += self._margin_x + self._grid_height += self._margin_y + + # Round up the grid size to the nearest cm + self._grid_width = math.ceil(self._grid_width / 10) * 10 + self._grid_height = math.ceil(self._grid_height / 10) * 10 + + self._offset_x = 0 + self._offset_y = 0 + self._findOptimalGridOffset() coord_initial_leftover_x = self._build_volume_bounding_box.right + 2 * self._grid_width coord_initial_leftover_y = (self._build_volume_bounding_box.back + self._build_volume_bounding_box.front) * 0.5 @@ -37,39 +48,38 @@ class GridArrange(Arranger): self._initial_leftover_grid_x = math.floor(self._initial_leftover_grid_x) self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y) - def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple[GroupedOperation, int]: # Find grid indexes that intersect with fixed objects - fixed_nodes_grid_ids = set() + self._fixed_nodes_grid_ids = set() for node in self._fixed_nodes: - fixed_nodes_grid_ids = fixed_nodes_grid_ids.union(self.intersectingGridIdxInclusive(node.getBoundingBox())) + self._fixed_nodes_grid_ids = self._fixed_nodes_grid_ids.union( + self.intersectingGridIdxInclusive(node.getBoundingBox())) - build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) + self._build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) # Filter out the corner grid squares if the build plate shape is elliptic if self._build_volume.getShape() == "elliptic": - build_plate_grid_ids = set(filter(lambda grid_id: self.checkGridUnderDiscSpace(grid_id[0], grid_id[1]), build_plate_grid_ids)) + self._build_plate_grid_ids = set( + filter(lambda grid_id: self.checkGridUnderDiscSpace(grid_id[0], grid_id[1]), + self._build_plate_grid_ids)) - allowed_grid_idx = build_plate_grid_ids.difference(fixed_nodes_grid_ids) + self._allowed_grid_idx = self._build_plate_grid_ids.difference(self._fixed_nodes_grid_ids) + def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple[GroupedOperation, int]: # Find the sequence in which items are placed coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back grid_build_plate_center_x, grid_build_plate_center_y = self.coordSpaceToGridSpace(coord_build_plate_center_x, coord_build_plate_center_y) - def distToCenter(grid_id: Tuple[int, int]) -> float: - grid_x, grid_y = grid_id - distance_squared = (grid_build_plate_center_x - grid_x) ** 2 + (grid_build_plate_center_y - grid_y) ** 2 - return distance_squared - - sequence: List[Tuple[int, int]] = list(allowed_grid_idx) - sequence.sort(key=distToCenter) + sequence: List[Tuple[int, int]] = list(self._allowed_grid_idx) + sequence.sort(key=lambda grid_id: (grid_build_plate_center_x - grid_id[0]) ** 2 + ( + grid_build_plate_center_y - grid_id[1]) ** 2) scene_root = Application.getInstance().getController().getScene().getRoot() grouped_operation = GroupedOperation() for grid_id, node in zip(sequence, self._nodes_to_arrange): grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) grid_x, grid_y = grid_id - operation = self.moveNodeOnGrid(node, grid_x, grid_y) + operation = self._moveNodeOnGrid(node, grid_x, grid_y) grouped_operation.addOperation(operation) leftover_nodes = self._nodes_to_arrange[len(sequence):] @@ -79,18 +89,103 @@ class GridArrange(Arranger): if add_new_nodes_in_scene: grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) # find the first next grid position that isn't occupied by a fixed node - while (self._initial_leftover_grid_x, left_over_grid_y) in fixed_nodes_grid_ids: + while (self._initial_leftover_grid_x, left_over_grid_y) in self._fixed_nodes_grid_ids: left_over_grid_y = left_over_grid_y - 1 - operation = self.moveNodeOnGrid(node, self._initial_leftover_grid_x, left_over_grid_y) + operation = self._moveNodeOnGrid(node, self._initial_leftover_grid_x, left_over_grid_y) grouped_operation.addOperation(operation) left_over_grid_y = left_over_grid_y - 1 + return grouped_operation, len(leftover_nodes) - def moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation": - coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) - center_grid_x = coord_grid_x + (0.5 * (self._grid_width + self._offset_x)) - center_grid_y = coord_grid_y + (0.5 * (self._grid_height + self._offset_y)) + def _findOptimalGridOffset(self): + if len(self._fixed_nodes) == 0: + self._offset_x = 0 + self._offset_y = 0 + return + + if len(self._fixed_nodes) == 1: + center_grid_x = 0.5 * self._grid_width + self._build_volume_bounding_box.left + center_grid_y = 0.5 * self._grid_height + self._build_volume_bounding_box.back + + bounding_box = self._fixed_nodes[0].getBoundingBox() + center_node_x = (bounding_box.left + bounding_box.right) * 0.5 + center_node_y = (bounding_box.back + bounding_box.front) * 0.5 + + self._offset_x = center_node_x - center_grid_x + self._offset_y = center_node_y - center_grid_y + + return + + class Event: + def __init__(self, coord: float, change: float): + self.coord = coord + self.change = change + + events_horizontal: List[Event] = [] + events_vertical: List[Event] = [] + + for node in self._fixed_nodes: + bounding_box = node.getBoundingBox() + + left = bounding_box.left - self._build_volume_bounding_box.left + right = bounding_box.right - self._build_volume_bounding_box.left + back = bounding_box.back - self._build_volume_bounding_box.back + front = bounding_box.front - self._build_volume_bounding_box.back + + value_left = math.ceil(left / self._grid_width) * self._grid_width - left + value_right = math.ceil(right / self._grid_width) * self._grid_width - right + value_back = math.ceil(back / self._grid_height) * self._grid_height - back + value_front = math.ceil(front / self._grid_height) * self._grid_height - front + + # give nodes a weight according to their size. This + # weight is heuristically chosen to be proportional to + # the number of grid squares the node-boundary occupies + weight = bounding_box.width + bounding_box.depth + + events_horizontal.append(Event(value_left, weight)) + events_horizontal.append(Event(value_right, -weight)) + events_vertical.append(Event(value_back, weight)) + events_vertical.append(Event(value_front, -weight)) + + events_horizontal.sort(key=lambda event: event.coord) + events_vertical.sort(key=lambda event: event.coord) + + def findOptimalOffsetAxis(events: List[Event], interval: float) -> float: + prev_coord = events[-1].coord - interval + + current_offset = 0 + + best_offset = float('inf') + best_coord_length = float('-inf') + best_coord = 0.0 + + for event in events: + coord_length = event.coord - prev_coord + + if current_offset < best_offset or (current_offset == best_offset and coord_length > best_coord_length): + best_offset = current_offset + best_coord_length = coord_length + best_coord = event.coord + + current_offset += event.change + prev_coord = event.coord + + return best_coord - best_coord_length * 0.5 + + center_grid_x = 0.5 * self._grid_width + center_grid_y = 0.5 * self._grid_height + + optimal_center_x = self._grid_width - findOptimalOffsetAxis(events_horizontal, self._grid_width) + optimal_center_y = self._grid_height - findOptimalOffsetAxis(events_vertical, self._grid_height) + + self._offset_x = optimal_center_x - center_grid_x + self._offset_y = optimal_center_y - center_grid_y + + def _moveNodeOnGrid(self, node: "SceneNode", grid_x: int, grid_y: int) -> "Operation.Operation": + coord_grid_x, coord_grid_y = self._gridSpaceToCoordSpace(grid_x, grid_y) + center_grid_x = coord_grid_x + (0.5 * self._grid_width) + center_grid_y = coord_grid_y + (0.5 * self._grid_height) bounding_box = node.getBoundingBox() center_node_x = (bounding_box.left + bounding_box.right) * 0.5 @@ -101,7 +196,7 @@ class GridArrange(Arranger): return TranslateOperation(node, Vector(delta_x, 0, delta_y)) - def getGridCornerPoints(self, bounding_box: "BoundingVolume") -> Tuple[float, float, float, float]: + def _getGridCornerPoints(self, bounding_box: "BoundingVolume") -> Tuple[float, float, float, float]: coord_x1 = bounding_box.left coord_x2 = bounding_box.right coord_y1 = bounding_box.back @@ -111,7 +206,7 @@ class GridArrange(Arranger): return grid_x1, grid_y1, grid_x2, grid_y2 def intersectingGridIdxInclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: - grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) + grid_x1, grid_y1, grid_x2, grid_y2 = self._getGridCornerPoints(bounding_box) grid_idx = set() for grid_x in range(math.floor(grid_x1), math.ceil(grid_x2)): for grid_y in range(math.floor(grid_y1), math.ceil(grid_y2)): @@ -119,26 +214,26 @@ class GridArrange(Arranger): return grid_idx def intersectingGridIdxExclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: - grid_x1, grid_y1, grid_x2, grid_y2 = self.getGridCornerPoints(bounding_box) + grid_x1, grid_y1, grid_x2, grid_y2 = self._getGridCornerPoints(bounding_box) grid_idx = set() for grid_x in range(math.ceil(grid_x1), math.floor(grid_x2)): for grid_y in range(math.ceil(grid_y1), math.floor(grid_y2)): grid_idx.add((grid_x, grid_y)) return grid_idx - def gridSpaceToCoordSpace(self, x: float, y: float) -> Tuple[float, float]: - grid_x = x * (self._grid_width + self._offset_x) + self._build_volume_bounding_box.left - grid_y = y * (self._grid_height + self._offset_y) + self._build_volume_bounding_box.back + def _gridSpaceToCoordSpace(self, x: float, y: float) -> Tuple[float, float]: + grid_x = x * self._grid_width + self._build_volume_bounding_box.left + self._offset_x + grid_y = y * self._grid_height + self._build_volume_bounding_box.back + self._offset_y return grid_x, grid_y def coordSpaceToGridSpace(self, grid_x: float, grid_y: float) -> Tuple[float, float]: - coord_x = (grid_x - self._build_volume_bounding_box.left) / (self._grid_width + self._offset_x) - coord_y = (grid_y - self._build_volume_bounding_box.back) / (self._grid_height + self._offset_y) + coord_x = (grid_x - self._build_volume_bounding_box.left - self._offset_x) / self._grid_width + coord_y = (grid_y - self._build_volume_bounding_box.back - self._offset_y) / self._grid_height return coord_x, coord_y def checkGridUnderDiscSpace(self, grid_x: int, grid_y: int) -> bool: - left, back = self.gridSpaceToCoordSpace(grid_x, grid_y) - right, front = self.gridSpaceToCoordSpace(grid_x + 1, grid_y + 1) + left, back = self._gridSpaceToCoordSpace(grid_x, grid_y) + right, front = self._gridSpaceToCoordSpace(grid_x + 1, grid_y + 1) corners = [(left, back), (right, back), (right, front), (left, front)] return all([self.checkPointUnderDiscSpace(x, y) for x, y in corners]) @@ -165,15 +260,14 @@ class GridArrange(Arranger): disc_y = ((y - self._build_volume_bounding_box.back) / self._build_volume_bounding_box.depth) * 2.0 - 1.0 return disc_x, disc_y - def drawDebugSvg(self): + def _drawDebugSvg(self): with open("Builvolume_test.svg", "w") as f: build_volume_bounding_box = self._build_volume_bounding_box f.write( f"\n") - ellipse = True - if ellipse: + if self._build_volume.getShape() == "elliptic": f.write( f""" """) else: @@ -196,30 +290,32 @@ class GridArrange(Arranger): /> """) - for grid_x in range(0, 100): - for grid_y in range(0, 100): - # if (grid_x, grid_y) in intersecting_grid_idx: - # fill_color = "red" - # elif (grid_x, grid_y) in build_plate_grid_idx: - # fill_color = "green" - # else: - # fill_color = "orange" + for grid_x in range(-10, 10): + for grid_y in range(-10, 10): + if (grid_x, grid_y) in self._allowed_grid_idx: + fill_color = "rgba(0, 255, 0, 0.5)" + elif (grid_x, grid_y) in self._build_plate_grid_ids: + fill_color = "rgba(255, 165, 0, 0.5)" + else: + fill_color = "rgba(255, 0, 0, 0.5)" - coord_grid_x, coord_grid_y = self.gridSpaceToCoordSpace(grid_x, grid_y) + coord_grid_x, coord_grid_y = self._gridSpaceToCoordSpace(grid_x, grid_y) f.write( f""" """) f.write(f""" @@ -237,24 +333,25 @@ class GridArrange(Arranger): fill="red" /> """) - for node in self._nodes_to_arrange: - bounding_box = node.getBoundingBox() - f.write(f""" - - """) - for x in range(math.floor(self._build_volume_bounding_box.left), math.floor(self._build_volume_bounding_box.right), 50): - for y in range(math.floor(self._build_volume_bounding_box.back), math.floor(self._build_volume_bounding_box.front), 50): - color = "green" if self.checkPointUnderDiscSpace(x, y) else "red" - f.write(f""" - - """) - f.write(f"") \ No newline at end of file + f.write(f""" + """) + + # coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left + # coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back + # f.write(f""" + # """) + + f.write(f"") From 719b11655ce3e002f04b065439ae3e80ab4e5d73 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 24 Aug 2023 11:24:21 +0200 Subject: [PATCH 15/30] Fix ctrl+z issues CURA-7951` --- cura/Arranging/Arranger.py | 4 ++-- cura/Arranging/GridArrange.py | 2 +- cura/Arranging/Nest2DArrange.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cura/Arranging/Arranger.py b/cura/Arranging/Arranger.py index 684fa1258b..fd93ab1846 100644 --- a/cura/Arranging/Arranger.py +++ b/cura/Arranging/Arranger.py @@ -5,7 +5,7 @@ if TYPE_CHECKING: class Arranger: - def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple["GroupedOperation", int]: + def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = False) -> Tuple["GroupedOperation", int]: """ Find placement for a set of scene nodes, but don't actually move them just yet. :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations @@ -16,7 +16,7 @@ class Arranger: """ raise NotImplementedError - def arrange(self, add_new_nodes_in_scene: bool = True) -> bool: + def arrange(self, add_new_nodes_in_scene: bool = False) -> bool: """ Find placement for a set of scene nodes, and move them by using a single grouped operation. :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 4866e99b69..9c7f0d31fb 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -37,7 +37,7 @@ class GridArrange(Arranger): self._initial_leftover_grid_x = math.floor(self._initial_leftover_grid_x) self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y) - def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple[GroupedOperation, int]: + def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = False) -> Tuple[GroupedOperation, int]: # Find grid indexes that intersect with fixed objects fixed_nodes_grid_ids = set() for node in self._fixed_nodes: diff --git a/cura/Arranging/Nest2DArrange.py b/cura/Arranging/Nest2DArrange.py index ea45fe0b8e..bad57c5045 100644 --- a/cura/Arranging/Nest2DArrange.py +++ b/cura/Arranging/Nest2DArrange.py @@ -124,7 +124,7 @@ class Nest2DArrange(Arranger): return found_solution_for_all, node_items - def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = True) -> Tuple[GroupedOperation, int]: + def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = False) -> Tuple[GroupedOperation, int]: scene_root = Application.getInstance().getController().getScene().getRoot() found_solution_for_all, node_items = self.findNodePlacement() From 1591a2a0c37cd8dc7a443a6f14101ddbf6e605af Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 24 Aug 2023 11:48:43 +0200 Subject: [PATCH 16/30] ctrl+z for grid CURA-7951 --- cura/Arranging/GridArrange.py | 3 ++- cura/CuraActions.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 9c7f0d31fb..c430de4d73 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -67,7 +67,8 @@ class GridArrange(Arranger): grouped_operation = GroupedOperation() for grid_id, node in zip(sequence, self._nodes_to_arrange): - grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) + if add_new_nodes_in_scene: + grouped_operation.addOperation(AddSceneNodeOperation(node, scene_root)) grid_x, grid_y = grid_id operation = self.moveNodeOnGrid(node, grid_x, grid_y) grouped_operation.addOperation(operation) diff --git a/cura/CuraActions.py b/cura/CuraActions.py index 29f50d88b2..9a61a1c4f0 100644 --- a/cura/CuraActions.py +++ b/cura/CuraActions.py @@ -243,7 +243,7 @@ class CuraActions(QObject): # Add the new nodes to the scene, and arrange them arranger = GridArrange(nodes, application.getBuildVolume(), fixed_nodes) - group_operation, not_fit_count = arranger.createGroupOperationForArrange() + group_operation, not_fit_count = arranger.createGroupOperationForArrange(add_new_nodes_in_scene = True) group_operation.push() # deselect currently selected nodes, and select the new nodes From ac78de1227329a35b259cf76a391012cc0699ffc Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 24 Aug 2023 11:55:07 +0200 Subject: [PATCH 17/30] Updated comments CURA-7951 --- cura/Arranging/GridArrange.py | 69 ++++++++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 23c14e248b..3b00f44ddc 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -118,11 +118,40 @@ class GridArrange(Arranger): return + # If there are multiple fixed nodes, an optimal solution is not always possible + # We will try to find an offset that minimizes the number of grid intersections + # with fixed nodes. The algorithm below achieves this by utilizing a scanline + # algorithm. In this algorithm each axis is solved separately as offsetting + # is completely independent in each axis. The comments explaining the algorithm + # below are for the x-axis, but the same applies for the y-axis. + # + # Each node either occupies ceil((node.right - node.right) / grid_width) or + # ceil((node.right - node.right) / grid_width) + 1 grid squares. We will call + # these the node's "footprint". + # + # ┌────────────────┐ + # minimum food print │ NODE │ + # └────────────────┘ + # │ grid 1 │ grid 2 │ grid 3 │ grid 4 | grid 5 | + # ┌────────────────┐ + # maximum food print │ NODE │ + # └────────────────┘ + # + # The algorithm will find the grid offset such that the number of nodes with + # a _minimal_ footprint is _maximized_. + + # The scanline algorithm works as follows, we create events for both end points + # of each node's footprint. The event have two properties, + # - the coordinate: the amount the endpoint can move to the + # left before it crosses a grid line + # - the change: either +1 or -1, indicating whether crossing the grid line + # would result in a minimal footprint node becoming a maximal footprint class Event: def __init__(self, coord: float, change: float): self.coord = coord self.change = change + # create events for both the horizontal and vertical axis events_horizontal: List[Event] = [] events_vertical: List[Event] = [] @@ -152,33 +181,39 @@ class GridArrange(Arranger): events_horizontal.sort(key=lambda event: event.coord) events_vertical.sort(key=lambda event: event.coord) - def findOptimalOffsetAxis(events: List[Event], interval: float) -> float: - prev_coord = events[-1].coord - interval + def findOptimalShiftAxis(events: List[Event], interval: float) -> float: + # executing the actual scanline algorithm + # iteratively go through events (left to right) and keep track of the + # current footprint. The optimal location is the one with the minimal + # footprint. If there are multiple locations with the same minimal + # footprint, the optimal location is the one with the largest range + # between the left and right endpoint of the footprint. + prev_offset = events[-1].coord - interval + current_minimal_footprint_count = 0 - current_offset = 0 - - best_offset = float('inf') - best_coord_length = float('-inf') - best_coord = 0.0 + best_minimal_footprint_count = float('inf') + best_offset_span = float('-inf') + best_offset = 0.0 for event in events: - coord_length = event.coord - prev_coord + offset_span = event.coord - prev_offset - if current_offset < best_offset or (current_offset == best_offset and coord_length > best_coord_length): - best_offset = current_offset - best_coord_length = coord_length - best_coord = event.coord + if current_minimal_footprint_count < best_minimal_footprint_count or ( + current_minimal_footprint_count == best_minimal_footprint_count and offset_span > best_offset_span): + best_minimal_footprint_count = current_minimal_footprint_count + best_offset_span = offset_span + best_offset = event.coord - current_offset += event.change - prev_coord = event.coord + current_minimal_footprint_count += event.change + prev_offset = event.coord - return best_coord - best_coord_length * 0.5 + return best_offset - best_offset_span * 0.5 center_grid_x = 0.5 * self._grid_width center_grid_y = 0.5 * self._grid_height - optimal_center_x = self._grid_width - findOptimalOffsetAxis(events_horizontal, self._grid_width) - optimal_center_y = self._grid_height - findOptimalOffsetAxis(events_vertical, self._grid_height) + optimal_center_x = self._grid_width - findOptimalShiftAxis(events_horizontal, self._grid_width) + optimal_center_y = self._grid_height - findOptimalShiftAxis(events_vertical, self._grid_height) self._offset_x = optimal_center_x - center_grid_x self._offset_y = optimal_center_y - center_grid_y From 34aa631531c55bf2e3b24e9ddb6bead061011f19 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 24 Aug 2023 16:13:39 +0200 Subject: [PATCH 18/30] Re-add arrange to CuraApplication CURA-7951 --- cura/CuraApplication.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 54b717b3d0..5f2770c3a2 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -114,6 +114,7 @@ from . import CameraAnimation from . import CuraActions from . import PlatformPhysics from . import PrintJobPreviewImageProvider +from .Arranging.Nest2DArrange import Nest2DArrange from .AutoSave import AutoSave from .Machines.Models.CompatibleMachineModel import CompatibleMachineModel from .Machines.Models.MachineListModel import MachineListModel @@ -1974,7 +1975,8 @@ class CuraApplication(QtApplication): if select_models_on_load: Selection.add(node) try: - arrange(nodes_to_arrange, self.getBuildVolume(), fixed_nodes) + arranger = Nest2DArrange(nodes_to_arrange, self.getBuildVolume(), fixed_nodes) + arranger.arrange() except: Logger.logException("e", "Failed to arrange the models") From e8c06ec7838efc89c0c17f30c67be6c02700d7ae Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 24 Aug 2023 16:34:34 +0200 Subject: [PATCH 19/30] grid arrange adjusted for disallowed area CURA-7951 --- cura/Arranging/GridArrange.py | 52 +++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 3b00f44ddc..a049c20c82 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -54,6 +54,11 @@ class GridArrange(Arranger): self._fixed_nodes_grid_ids = self._fixed_nodes_grid_ids.union( self.intersectingGridIdxInclusive(node.getBoundingBox())) + #grid indexes that are in disallowed area + for polygon in self._build_volume.getDisallowedAreas(): + self._fixed_nodes_grid_ids = self._fixed_nodes_grid_ids.union( + self._getIntersectingGridIdForPolygon(polygon)) + self._build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) # Filter out the corner grid squares if the build plate shape is elliptic @@ -241,6 +246,32 @@ class GridArrange(Arranger): grid_x2, grid_y2 = self.coordSpaceToGridSpace(coord_x2, coord_y2) return grid_x1, grid_y1, grid_x2, grid_y2 + def _getIntersectingGridIdForPolygon(self, polygon)-> Set[Tuple[int, int]]: + # (x0, y0) + # | + # v + # ┌─────────────┐ + # │ │ + # │ │ + # └─────────────┘ < (x1, y1) + x0 = float('inf') + y0 = float('inf') + x1 = float('-inf') + y1 = float('-inf') + grid_idx = set() + for [x, y] in polygon.getPoints(): + x0 = min(x0, x) + y0 = min(y0, y) + x1 = max(x1, x) + y1 = max(y1, y) + grid_x1, grid_y1 = self.coordSpaceToGridSpace(x0, y0) + grid_x2, grid_y2 = self.coordSpaceToGridSpace(x1, y1) + + for grid_x in range(math.floor(grid_x1), math.ceil(grid_x2)): + for grid_y in range(math.floor(grid_y1), math.ceil(grid_y2)): + grid_idx.add((grid_x, grid_y)) + return grid_idx + def intersectingGridIdxInclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: grid_x1, grid_y1, grid_x2, grid_y2 = self._getGridCornerPoints(bounding_box) grid_idx = set() @@ -326,8 +357,25 @@ class GridArrange(Arranger): /> """) - for grid_x in range(-10, 10): - for grid_y in range(-10, 10): + for polygon in self._build_volume.getDisallowedAreas(): + # Extract individual points and convert them to tuples + + path_data = "" + for [x,y] in polygon.getPoints(): + path_data += f"{x},{y} " + + f.write( + f""" + + """) + + for grid_x in range(-10, 100): + for grid_y in range(-10, 100): if (grid_x, grid_y) in self._allowed_grid_idx: fill_color = "rgba(0, 255, 0, 0.5)" elif (grid_x, grid_y) in self._build_plate_grid_ids: From 6e045e8b37619cea7ff484e6e99b857637204f8c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 29 Aug 2023 14:11:36 +0200 Subject: [PATCH 20/30] Put back `getAnyExtruderPositionWithOrDefault` api function CURA-10685 --- cura/CuraApplication.py | 2 ++ cura/Settings/CuraFormulaFunctions.py | 9 +++++++++ docs/profiles/getting_a_setting_value.md | 3 +++ 3 files changed, 14 insertions(+) diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index f44393657f..0de3ccc780 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -410,6 +410,8 @@ class CuraApplication(QtApplication): SettingFunction.registerOperator("extruderValue", self._cura_formula_functions.getValueInExtruder) SettingFunction.registerOperator("extruderValues", self._cura_formula_functions.getValuesInAllExtruders) SettingFunction.registerOperator("anyExtruderWithMaterial", self._cura_formula_functions.getExtruderPositionWithMaterial) + SettingFunction.registerOperator("anyExtruderNrWithOrDefault", + self._cura_formula_functions.getAnyExtruderPositionWithOrDefault) SettingFunction.registerOperator("resolveOrValue", self._cura_formula_functions.getResolveOrValue) SettingFunction.registerOperator("defaultExtruderPosition", self._cura_formula_functions.getDefaultExtruderPosition) SettingFunction.registerOperator("valueFromContainer", self._cura_formula_functions.getValueFromContainerAtIndex) diff --git a/cura/Settings/CuraFormulaFunctions.py b/cura/Settings/CuraFormulaFunctions.py index 08c24180bb..d7b6228e09 100644 --- a/cura/Settings/CuraFormulaFunctions.py +++ b/cura/Settings/CuraFormulaFunctions.py @@ -97,6 +97,15 @@ class CuraFormulaFunctions: return result + # Get the first extruder that adheres to a specific (boolean) property, like 'material_is_support_material'. + def getAnyExtruderPositionWithOrDefault(self, filter_key: str, + context: Optional["PropertyEvaluationContext"] = None) -> str: + for extruder in self._getActiveExtruders(context): + value = extruder.getRawProperty(filter_key, "value", context=context) + if value is None or not value: + continue + return str(extruder.position) + # Get the first extruder with material that adheres to a specific (boolean) property, like 'material_is_support_material'. def getExtruderPositionWithMaterial(self, filter_key: str, context: Optional["PropertyEvaluationContext"] = None) -> str: diff --git a/docs/profiles/getting_a_setting_value.md b/docs/profiles/getting_a_setting_value.md index ab18be0f42..7cd912ac45 100644 --- a/docs/profiles/getting_a_setting_value.md +++ b/docs/profiles/getting_a_setting_value.md @@ -54,6 +54,9 @@ There are also a few extra things that can be used in these expressions: * The function `extruderValue(extruder, key)` will evaluate a particular setting for a particular extruder. * The function `resolveOrValue(key)` will perform the full setting evaluation as described in this document for the current context (so if this setting is being evaluated for the second extruder it would perform it as if coming from the second extruder). * The function `defaultExtruderPosition()` will get the first extruder that is not disabled. For instance, if a printer has three extruders but the first is disabled, this would return `1` to indicate the second extruder (0-indexed). +* The function `anyExtruderNrWithOrDefault(key)` will filter the list of extruders on the key, and then give the first + index for which it is true, or if none of them are, the default one as specified by the 'default extruder position' + function above. * The function `anyExtruderWithMaterial(key)` will filter the list of extruders on the key of material quality, and then give the first index for which it is true, or if none of them are, the default one as specified by the 'default extruder position' function above. * The function `valueFromContainer(key, index)` will get a setting value from the global stack, but skip the first few containers in that stack. It will skip until it reaches a particular index in the container stack. * The function `extruderValueFromContainer(key, index)` will get a setting value from the current extruder stack, but skip the first few containers in that stack. It will skip until it reaches a particular index in the container stack. From 0397095789dad159e6ff25eae06a31cbe679941a Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 29 Aug 2023 15:18:59 +0200 Subject: [PATCH 21/30] pyarcus pynest2d and pysavitar version changed conan build fix --- conanfile.py | 66 +++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/conanfile.py b/conanfile.py index 77cecf1134..3c836dae2a 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,7 +10,7 @@ from conan.tools.env import VirtualRunEnv, Environment, VirtualBuildEnv from conan.tools.scm import Version from conan.errors import ConanInvalidConfiguration, ConanException -required_conan_version = ">=1.54 <=1.56.0 || >=1.58.0 <2.0.0" +required_conan_version = ">=1.58.0 <2.0.0" class CuraConan(ConanFile): @@ -293,6 +293,7 @@ class CuraConan(ConanFile): self.options["pysavitar"].shared = True self.options["pynest2d"].shared = True self.options["cpython"].shared = True + self.options["boost"].header_only = True def validate(self): version = self.conf_info.get("user.cura:version", default = self.version, check_type = str) @@ -300,10 +301,11 @@ class CuraConan(ConanFile): raise ConanInvalidConfiguration("Only versions 5+ are support") def requirements(self): - self.requires("pyarcus/5.2.2") + self.requires("boost/1.82.0") + self.requires("pyarcus/(latest)@ultimaker/cura_10951") self.requires("curaengine/(latest)@ultimaker/testing") - self.requires("pysavitar/5.2.2") - self.requires("pynest2d/5.2.2") + self.requires("pysavitar/(latest)@ultimaker/cura_10951") + self.requires("pynest2d/(latest)@ultimaker/cura_10951") self.requires("uranium/(latest)@ultimaker/testing") self.requires("cura_binary_data/(latest)@ultimaker/testing") self.requires("cpython/3.10.4") @@ -338,7 +340,38 @@ class CuraConan(ConanFile): vr.generate() self._generate_cura_version(os.path.join(self.source_folder, "cura")) + self._generate_about_versions(os.path.join(self.source_folder, "resources","qml", "Dialogs")) + if not self.in_local_cache: + # Copy CuraEngine.exe to bindirs of Virtual Python Environment + curaengine = self.dependencies["curaengine"].cpp_info + copy(self, "CuraEngine.exe", curaengine.bindirs[0], self.source_folder, keep_path = False) + copy(self, "CuraEngine", curaengine.bindirs[0], self.source_folder, keep_path = False) + + # Copy resources of cura_binary_data + cura_binary_data = self.dependencies["cura_binary_data"].cpp_info + copy(self, "*", cura_binary_data.resdirs[0], str(self._share_dir.joinpath("cura")), keep_path = True) + copy(self, "*", cura_binary_data.resdirs[1], str(self._share_dir.joinpath("uranium")), keep_path = True) + if self.settings.os == "Windows": + copy(self, "*", cura_binary_data.resdirs[2], str(self._share_dir.joinpath("windows")), keep_path = True) + + for dependency in self.dependencies.host.values(): + for bindir in dependency.cpp_info.bindirs: + copy(self, "*.dll", bindir, str(self._site_packages), keep_path = False) + for libdir in dependency.cpp_info.libdirs: + copy(self, "*.pyd", libdir, str(self._site_packages), keep_path = False) + copy(self, "*.pyi", libdir, str(self._site_packages), keep_path = False) + copy(self, "*.dylib", libdir, str(self._base_dir.joinpath("lib")), keep_path = False) + + # Copy materials (flat) + rmdir(self, os.path.join(self.source_folder, "resources", "materials")) + fdm_materials = self.dependencies["fdm_materials"].cpp_info + copy(self, "*", fdm_materials.resdirs[0], str(self._share_dir.joinpath("cura"))) + + # Copy internal resources + if self.options.internal: + cura_private_data = self.dependencies["cura_private_data"].cpp_info + copy(self, "*", cura_private_data.resdirs[0], str(self._share_dir.joinpath("cura"))) if self.options.devtools: entitlements_file = "'{}'".format(os.path.join(self.source_folder, "packaging", "MacOS", "cura.entitlements")) @@ -357,8 +390,6 @@ class CuraConan(ConanFile): pot = self.python_requires["translationextractor"].module.ExtractTranslations(self, cpp_info.bindirs[0]) pot.generate() - self._generate_about_versions(os.path.join(self.source_folder, "resources","qml", "Dialogs")) - def build(self): if self.options.devtools: if self.settings.os != "Windows" or self.conf.get("tools.microsoft.bash:path", check_type = str): @@ -370,29 +401,6 @@ class CuraConan(ConanFile): cpp_info = self.dependencies["gettext"].cpp_info self.run(f"{cpp_info.bindirs[0]}/msgfmt {po_file} -o {mo_file} -f", env="conanbuild", ignore_errors=True) - def imports(self): - self.copy("CuraEngine.exe", root_package = "curaengine", src = "@bindirs", dst = "", keep_path = False) - self.copy("CuraEngine", root_package = "curaengine", src = "@bindirs", dst = "", keep_path = False) - - rmdir(self, os.path.join(self.source_folder, "resources", "materials")) - self.copy("*.fdm_material", root_package = "fdm_materials", src = "@resdirs", dst = "resources/materials", keep_path = False) - self.copy("*.sig", root_package = "fdm_materials", src = "@resdirs", dst = "resources/materials", keep_path = False) - - if self.options.internal: - self.copy("*", root_package = "cura_private_data", src = self.deps_cpp_info["cura_private_data"].resdirs[0], - dst = self._share_dir.joinpath("cura", "resources"), keep_path = True) - - # Copy resources of cura_binary_data - self.copy("*", root_package = "cura_binary_data", src = self.deps_cpp_info["cura_binary_data"].resdirs[0], - dst = self._share_dir.joinpath("cura", "resources"), keep_path = True) - self.copy("*", root_package = "cura_binary_data", src = self.deps_cpp_info["cura_binary_data"].resdirs[1], - dst =self._share_dir.joinpath("uranium", "resources"), keep_path = True) - - self.copy("*.dll", src = "@bindirs", dst = self._site_packages) - self.copy("*.pyd", src = "@libdirs", dst = self._site_packages) - self.copy("*.pyi", src = "@libdirs", dst = self._site_packages) - self.copy("*.dylib", src = "@libdirs", dst = self._script_dir) - def deploy(self): # Copy CuraEngine.exe to bindirs of Virtual Python Environment curaengine = self.dependencies["curaengine"].cpp_info From d80e349f0b22985b027ecd376242cf0b5b4f1652 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 29 Aug 2023 15:27:47 +0200 Subject: [PATCH 22/30] source_folder as root for materials conan build fix --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 3c836dae2a..1ab7d939af 100644 --- a/conanfile.py +++ b/conanfile.py @@ -366,7 +366,7 @@ class CuraConan(ConanFile): # Copy materials (flat) rmdir(self, os.path.join(self.source_folder, "resources", "materials")) fdm_materials = self.dependencies["fdm_materials"].cpp_info - copy(self, "*", fdm_materials.resdirs[0], str(self._share_dir.joinpath("cura"))) + copy(self, "*", fdm_materials.resdirs[0], self.source_folder) # Copy internal resources if self.options.internal: From cfc4db00a533dafd5237e26de488e58c192e4482 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 12:41:53 +0200 Subject: [PATCH 23/30] Use descriptive variable name instead of magic number CURA-7951 --- cura/Arranging/GridArrange.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index a049c20c82..d046114fab 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -35,8 +35,9 @@ class GridArrange(Arranger): self._grid_height += self._margin_y # Round up the grid size to the nearest cm - self._grid_width = math.ceil(self._grid_width / 10) * 10 - self._grid_height = math.ceil(self._grid_height / 10) * 10 + grid_precision = 10 # 1cm + self._grid_width = math.ceil(self._grid_width / grid_precision) * grid_precision + self._grid_height = math.ceil(self._grid_height / grid_precision) * grid_precision self._offset_x = 0 self._offset_y = 0 From b3b5ffbf5530ed21b351aaf2ea0e70234d0c0148 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 12:45:34 +0200 Subject: [PATCH 24/30] Put try catch around actual arranging `arrange` is the function that _could_ fail CURA-7951 --- cura/Arranging/ArrangeObjectsJob.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index b1e0432786..48d2436482 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -32,17 +32,18 @@ class ArrangeObjectsJob(Job): title = i18n_catalog.i18nc("@info:title", "Finding Location")) status_message.show() + if self._grid_arrange: + arranger = GridArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) + else: + arranger = Nest2DArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, + factor=1000) + + found_solution_for_all = False try: - if self._grid_arrange: - arranger = GridArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes) - else: - arranger = Nest2DArrange(self._nodes, Application.getInstance().getBuildVolume(), self._fixed_nodes, - factor=1000) - found_solution_for_all = arranger.arrange() - except: # If the thread crashes, the message should still close - Logger.logException("e", "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") + Logger.logException("e", + "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") status_message.hide() From 38a5754c6fd9b32a5fe080eb225367eac51b5452 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 12:47:56 +0200 Subject: [PATCH 25/30] Mark method functions as private CURA-7951 --- cura/Arranging/GridArrange.py | 36 ++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index d046114fab..d2eace3c26 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -45,7 +45,8 @@ class GridArrange(Arranger): coord_initial_leftover_x = self._build_volume_bounding_box.right + 2 * self._grid_width coord_initial_leftover_y = (self._build_volume_bounding_box.back + self._build_volume_bounding_box.front) * 0.5 - self._initial_leftover_grid_x, self._initial_leftover_grid_y = self.coordSpaceToGridSpace(coord_initial_leftover_x, coord_initial_leftover_y) + self._initial_leftover_grid_x, self._initial_leftover_grid_y = self._coordSpaceToGridSpace( + coord_initial_leftover_x, coord_initial_leftover_y) self._initial_leftover_grid_x = math.floor(self._initial_leftover_grid_x) self._initial_leftover_grid_y = math.floor(self._initial_leftover_grid_y) @@ -53,19 +54,19 @@ class GridArrange(Arranger): self._fixed_nodes_grid_ids = set() for node in self._fixed_nodes: self._fixed_nodes_grid_ids = self._fixed_nodes_grid_ids.union( - self.intersectingGridIdxInclusive(node.getBoundingBox())) + self._intersectingGridIdxInclusive(node.getBoundingBox())) #grid indexes that are in disallowed area for polygon in self._build_volume.getDisallowedAreas(): self._fixed_nodes_grid_ids = self._fixed_nodes_grid_ids.union( self._getIntersectingGridIdForPolygon(polygon)) - self._build_plate_grid_ids = self.intersectingGridIdxExclusive(self._build_volume_bounding_box) + self._build_plate_grid_ids = self._intersectingGridIdxExclusive(self._build_volume_bounding_box) # Filter out the corner grid squares if the build plate shape is elliptic if self._build_volume.getShape() == "elliptic": self._build_plate_grid_ids = set( - filter(lambda grid_id: self.checkGridUnderDiscSpace(grid_id[0], grid_id[1]), + filter(lambda grid_id: self._checkGridUnderDiscSpace(grid_id[0], grid_id[1]), self._build_plate_grid_ids)) self._allowed_grid_idx = self._build_plate_grid_ids.difference(self._fixed_nodes_grid_ids) @@ -74,7 +75,8 @@ class GridArrange(Arranger): # Find the sequence in which items are placed coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back - grid_build_plate_center_x, grid_build_plate_center_y = self.coordSpaceToGridSpace(coord_build_plate_center_x, coord_build_plate_center_y) + grid_build_plate_center_x, grid_build_plate_center_y = self._coordSpaceToGridSpace(coord_build_plate_center_x, + coord_build_plate_center_y) sequence: List[Tuple[int, int]] = list(self._allowed_grid_idx) sequence.sort(key=lambda grid_id: (grid_build_plate_center_x - grid_id[0]) ** 2 + ( @@ -243,8 +245,8 @@ class GridArrange(Arranger): coord_x2 = bounding_box.right coord_y1 = bounding_box.back coord_y2 = bounding_box.front - grid_x1, grid_y1 = self.coordSpaceToGridSpace(coord_x1, coord_y1) - grid_x2, grid_y2 = self.coordSpaceToGridSpace(coord_x2, coord_y2) + grid_x1, grid_y1 = self._coordSpaceToGridSpace(coord_x1, coord_y1) + grid_x2, grid_y2 = self._coordSpaceToGridSpace(coord_x2, coord_y2) return grid_x1, grid_y1, grid_x2, grid_y2 def _getIntersectingGridIdForPolygon(self, polygon)-> Set[Tuple[int, int]]: @@ -265,15 +267,15 @@ class GridArrange(Arranger): y0 = min(y0, y) x1 = max(x1, x) y1 = max(y1, y) - grid_x1, grid_y1 = self.coordSpaceToGridSpace(x0, y0) - grid_x2, grid_y2 = self.coordSpaceToGridSpace(x1, y1) + grid_x1, grid_y1 = self._coordSpaceToGridSpace(x0, y0) + grid_x2, grid_y2 = self._coordSpaceToGridSpace(x1, y1) for grid_x in range(math.floor(grid_x1), math.ceil(grid_x2)): for grid_y in range(math.floor(grid_y1), math.ceil(grid_y2)): grid_idx.add((grid_x, grid_y)) return grid_idx - def intersectingGridIdxInclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: + def _intersectingGridIdxInclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: grid_x1, grid_y1, grid_x2, grid_y2 = self._getGridCornerPoints(bounding_box) grid_idx = set() for grid_x in range(math.floor(grid_x1), math.ceil(grid_x2)): @@ -281,7 +283,7 @@ class GridArrange(Arranger): grid_idx.add((grid_x, grid_y)) return grid_idx - def intersectingGridIdxExclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: + def _intersectingGridIdxExclusive(self, bounding_box: "BoundingVolume") -> Set[Tuple[int, int]]: grid_x1, grid_y1, grid_x2, grid_y2 = self._getGridCornerPoints(bounding_box) grid_idx = set() for grid_x in range(math.ceil(grid_x1), math.floor(grid_x2)): @@ -294,23 +296,23 @@ class GridArrange(Arranger): grid_y = y * self._grid_height + self._build_volume_bounding_box.back + self._offset_y return grid_x, grid_y - def coordSpaceToGridSpace(self, grid_x: float, grid_y: float) -> Tuple[float, float]: + def _coordSpaceToGridSpace(self, grid_x: float, grid_y: float) -> Tuple[float, float]: coord_x = (grid_x - self._build_volume_bounding_box.left - self._offset_x) / self._grid_width coord_y = (grid_y - self._build_volume_bounding_box.back - self._offset_y) / self._grid_height return coord_x, coord_y - def checkGridUnderDiscSpace(self, grid_x: int, grid_y: int) -> bool: + def _checkGridUnderDiscSpace(self, grid_x: int, grid_y: int) -> bool: left, back = self._gridSpaceToCoordSpace(grid_x, grid_y) right, front = self._gridSpaceToCoordSpace(grid_x + 1, grid_y + 1) corners = [(left, back), (right, back), (right, front), (left, front)] - return all([self.checkPointUnderDiscSpace(x, y) for x, y in corners]) + return all([self._checkPointUnderDiscSpace(x, y) for x, y in corners]) - def checkPointUnderDiscSpace(self, x: float, y: float) -> bool: - disc_x, disc_y = self.coordSpaceToDiscSpace(x, y) + def _checkPointUnderDiscSpace(self, x: float, y: float) -> bool: + disc_x, disc_y = self._coordSpaceToDiscSpace(x, y) distance_to_center_squared = disc_x ** 2 + disc_y ** 2 return distance_to_center_squared <= 1.0 - def coordSpaceToDiscSpace(self, x: float, y: float) -> Tuple[float, float]: + def _coordSpaceToDiscSpace(self, x: float, y: float) -> Tuple[float, float]: # Transform coordinate system to # # coord_build_plate_left = -1 From 3e39bbdabd9f154dc6754d23c01006ad5a6cfd08 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 12:51:03 +0200 Subject: [PATCH 26/30] Typo CURA-7951 --- cura/Arranging/GridArrange.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index d2eace3c26..8a4606e3f3 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -138,11 +138,11 @@ class GridArrange(Arranger): # these the node's "footprint". # # ┌────────────────┐ - # minimum food print │ NODE │ + # minimum foot-print │ NODE │ # └────────────────┘ # │ grid 1 │ grid 2 │ grid 3 │ grid 4 | grid 5 | # ┌────────────────┐ - # maximum food print │ NODE │ + # maximum foot-print │ NODE │ # └────────────────┘ # # The algorithm will find the grid offset such that the number of nodes with From afc1ba78f5cf239a035a85ea25aff7efd63de7bf Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 12:52:27 +0200 Subject: [PATCH 27/30] Remove debugging utility function CURA-7951 --- cura/Arranging/GridArrange.py | 113 ---------------------------------- 1 file changed, 113 deletions(-) diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 8a4606e3f3..95ce2c7190 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -329,116 +329,3 @@ class GridArrange(Arranger): disc_x = ((x - self._build_volume_bounding_box.left) / self._build_volume_bounding_box.width) * 2.0 - 1.0 disc_y = ((y - self._build_volume_bounding_box.back) / self._build_volume_bounding_box.depth) * 2.0 - 1.0 return disc_x, disc_y - - def _drawDebugSvg(self): - with open("Builvolume_test.svg", "w") as f: - build_volume_bounding_box = self._build_volume_bounding_box - - f.write( - f"\n") - - if self._build_volume.getShape() == "elliptic": - f.write( - f""" - - """) - else: - f.write( - f""" - - """) - - for polygon in self._build_volume.getDisallowedAreas(): - # Extract individual points and convert them to tuples - - path_data = "" - for [x,y] in polygon.getPoints(): - path_data += f"{x},{y} " - - f.write( - f""" - - """) - - for grid_x in range(-10, 100): - for grid_y in range(-10, 100): - if (grid_x, grid_y) in self._allowed_grid_idx: - fill_color = "rgba(0, 255, 0, 0.5)" - elif (grid_x, grid_y) in self._build_plate_grid_ids: - fill_color = "rgba(255, 165, 0, 0.5)" - else: - fill_color = "rgba(255, 0, 0, 0.5)" - - coord_grid_x, coord_grid_y = self._gridSpaceToCoordSpace(grid_x, grid_y) - f.write( - f""" - - """) - f.write(f""" - - {grid_x},{grid_y} - - """) - for node in self._fixed_nodes: - bounding_box = node.getBoundingBox() - f.write(f""" - - """) - - f.write(f""" - """) - - # coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left - # coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back - # f.write(f""" - # """) - - f.write(f"") From 586739c5479c0363b98468b7fb679294870b3d0c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 30 Aug 2023 13:06:19 +0200 Subject: [PATCH 28/30] Avoid bool-trap CURA-7951 --- cura/Arranging/Arranger.py | 7 ++++--- cura/Arranging/GridArrange.py | 2 +- cura/Arranging/Nest2DArrange.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cura/Arranging/Arranger.py b/cura/Arranging/Arranger.py index fd93ab1846..f7f9870cf9 100644 --- a/cura/Arranging/Arranger.py +++ b/cura/Arranging/Arranger.py @@ -5,7 +5,7 @@ if TYPE_CHECKING: class Arranger: - def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = False) -> Tuple["GroupedOperation", int]: + def createGroupOperationForArrange(self, *, add_new_nodes_in_scene: bool = False) -> Tuple["GroupedOperation", int]: """ Find placement for a set of scene nodes, but don't actually move them just yet. :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations @@ -16,12 +16,13 @@ class Arranger: """ raise NotImplementedError - def arrange(self, add_new_nodes_in_scene: bool = False) -> bool: + def arrange(self, *, add_new_nodes_in_scene: bool = False) -> bool: """ Find placement for a set of scene nodes, and move them by using a single grouped operation. :param add_new_nodes_in_scene: Whether to create new scene nodes before applying the transformations and rotations :return: found_solution_for_all: Whether the algorithm found a place on the buildplate for all the objects """ - grouped_operation, not_fit_count = self.createGroupOperationForArrange(add_new_nodes_in_scene) + grouped_operation, not_fit_count = self.createGroupOperationForArrange( + add_new_nodes_in_scene=add_new_nodes_in_scene) grouped_operation.push() return not_fit_count == 0 diff --git a/cura/Arranging/GridArrange.py b/cura/Arranging/GridArrange.py index 95ce2c7190..493c81b27c 100644 --- a/cura/Arranging/GridArrange.py +++ b/cura/Arranging/GridArrange.py @@ -71,7 +71,7 @@ class GridArrange(Arranger): self._allowed_grid_idx = self._build_plate_grid_ids.difference(self._fixed_nodes_grid_ids) - def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = False) -> Tuple[GroupedOperation, int]: + def createGroupOperationForArrange(self, *, add_new_nodes_in_scene: bool = False) -> Tuple[GroupedOperation, int]: # Find the sequence in which items are placed coord_build_plate_center_x = self._build_volume_bounding_box.width * 0.5 + self._build_volume_bounding_box.left coord_build_plate_center_y = self._build_volume_bounding_box.depth * 0.5 + self._build_volume_bounding_box.back diff --git a/cura/Arranging/Nest2DArrange.py b/cura/Arranging/Nest2DArrange.py index bad57c5045..5fcd36c1a3 100644 --- a/cura/Arranging/Nest2DArrange.py +++ b/cura/Arranging/Nest2DArrange.py @@ -124,7 +124,7 @@ class Nest2DArrange(Arranger): return found_solution_for_all, node_items - def createGroupOperationForArrange(self, add_new_nodes_in_scene: bool = False) -> Tuple[GroupedOperation, int]: + def createGroupOperationForArrange(self, *, add_new_nodes_in_scene: bool = False) -> Tuple[GroupedOperation, int]: scene_root = Application.getInstance().getController().getScene().getRoot() found_solution_for_all, node_items = self.findNodePlacement() From 1fb7191dd76bbdcbceae47b9581f5e18945be395 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 31 Aug 2023 05:26:40 +0200 Subject: [PATCH 29/30] Use conf skip_test Contributes to CURA-10951 --- .github/workflows/conan-package-create.yml | 2 +- .github/workflows/conan-package.yml | 2 +- .github/workflows/conan-recipe-export.yml | 2 +- .github/workflows/requirements-conan-package.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/conan-package-create.yml b/.github/workflows/conan-package-create.yml index 701a978cd8..5df9f7377c 100644 --- a/.github/workflows/conan-package-create.yml +++ b/.github/workflows/conan-package-create.yml @@ -147,7 +147,7 @@ jobs: run: conan remote add cura-private https://ultimaker.jfrog.io/artifactory/api/conan/cura-private True - name: Create the Packages - run: conan install ${{ inputs.recipe_id_full }} --build=missing --update + run: conan install ${{ inputs.recipe_id_full }} --build=missing --update -c tools.build:skip_test=True - name: Upload the Package(s) if: ${{ always() && inputs.conan_upload_community }} diff --git a/.github/workflows/conan-package.yml b/.github/workflows/conan-package.yml index 9949621251..b5c135e23e 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/conan-package.yml @@ -114,7 +114,7 @@ jobs: run: conan config install https://github.com/Ultimaker/conan-config.git - name: Create the Packages - run: conan create . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} --build=missing --update -o ${{ needs.conan-recipe-version.outputs.project_name }}:devtools=True + run: conan create . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} --build=missing --update -o ${{ needs.conan-recipe-version.outputs.project_name }}:devtools=True -c tools.build:skip_test=True - name: Create the latest alias if: always() diff --git a/.github/workflows/conan-recipe-export.yml b/.github/workflows/conan-recipe-export.yml index 869a9de59e..4bd88a99c1 100644 --- a/.github/workflows/conan-recipe-export.yml +++ b/.github/workflows/conan-recipe-export.yml @@ -83,7 +83,7 @@ jobs: - name: Export the Package (binaries) if: ${{ inputs.conan_export_binaries }} - run: conan create . ${{ inputs.recipe_id_full }} --build=missing --update + run: conan create . ${{ inputs.recipe_id_full }} --build=missing --update -c tools.build:skip_test=True - name: Export the Package if: ${{ !inputs.conan_export_binaries }} diff --git a/.github/workflows/requirements-conan-package.txt b/.github/workflows/requirements-conan-package.txt index 6b4d4cffc8..9380d1cb98 100644 --- a/.github/workflows/requirements-conan-package.txt +++ b/.github/workflows/requirements-conan-package.txt @@ -1,2 +1,2 @@ -conan==1.60.2 +conan>=1.60.2,<2.0.0 sip From baafdbfce3740874e13bd4ec7ab574c0d9fda013 Mon Sep 17 00:00:00 2001 From: jellespijker Date: Thu, 31 Aug 2023 05:45:44 +0200 Subject: [PATCH 30/30] Use gcc-13 and new conan-config --- .github/workflows/conan-package-create.yml | 243 +++++----- .github/workflows/conan-package.yml | 29 +- .github/workflows/conan-recipe-export.yml | 161 +++---- .github/workflows/linux.yml | 513 ++++++++++----------- .github/workflows/unit-test.yml | 278 +++++------ .github/workflows/update-translation.yml | 141 +++--- 6 files changed, 683 insertions(+), 682 deletions(-) diff --git a/.github/workflows/conan-package-create.yml b/.github/workflows/conan-package-create.yml index 5df9f7377c..e8329fa7b1 100644 --- a/.github/workflows/conan-package-create.yml +++ b/.github/workflows/conan-package-create.yml @@ -1,158 +1,153 @@ name: Create and Upload Conan package on: - workflow_call: - inputs: - project_name: - required: true - type: string + workflow_call: + inputs: + project_name: + required: true + type: string - recipe_id_full: - required: true - type: string + recipe_id_full: + required: true + type: string - build_id: - required: true - type: number + build_id: + required: true + type: number - build_info: - required: false - default: true - type: boolean + build_info: + required: false + default: true + type: boolean - recipe_id_latest: - required: false - type: string + recipe_id_latest: + required: false + type: string - runs_on: - required: true - type: string + runs_on: + required: true + type: string - python_version: - required: true - type: string + python_version: + required: true + type: string - conan_config_branch: - required: false - type: string + conan_config_branch: + required: false + type: string - conan_logging_level: - required: false - type: string + conan_logging_level: + required: false + type: string - conan_clean_local_cache: - required: false - type: boolean - default: false + conan_clean_local_cache: + required: false + type: boolean + default: false - conan_upload_community: - required: false - default: true - type: boolean + conan_upload_community: + required: false + default: true + type: boolean env: - CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: ${{ inputs.conan_logging_level }} - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD: ${{ secrets.CONAN_PASS }} + CONAN_LOG_RUN_TO_OUTPUT: 1 + CONAN_LOGGING_LEVEL: ${{ inputs.conan_logging_level }} + CONAN_NON_INTERACTIVE: 1 jobs: - conan-package-create: - runs-on: ${{ inputs.runs_on }} + conan-package-create: + runs-on: ${{ inputs.runs_on }} - steps: - - name: Checkout - uses: actions/checkout@v3 + steps: + - name: Checkout + uses: actions/checkout@v3 - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: ${{ inputs.python_version }} - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt + - name: Setup Python and pip + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python_version }} + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-conan-package.txt - - name: Install Python requirements for runner - run: pip install -r https://raw.githubusercontent.com/Ultimaker/Cura/main/.github/workflows/requirements-conan-package.txt - # Note the runner requirements are always installed from the main branch in the Ultimaker/Cura repo + - name: Install Python requirements for runner + run: pip install -r https://raw.githubusercontent.com/Ultimaker/Cura/main/.github/workflows/requirements-conan-package.txt + # Note the runner requirements are always installed from the main branch in the Ultimaker/Cura repo - - name: Use Conan download cache (Bash) - if: ${{ runner.os != 'Windows' }} - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" + - name: Use Conan download cache (Bash) + if: ${{ runner.os != 'Windows' }} + run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - name: Use Conan download cache (Powershell) - if: ${{ runner.os == 'Windows' }} - run: conan config set storage.download_cache="C:\Users\runneradmin\.conan\conan_download_cache" + - name: Use Conan download cache (Powershell) + if: ${{ runner.os == 'Windows' }} + run: conan config set storage.download_cache="C:\Users\runneradmin\.conan\conan_download_cache" - - name: Cache Conan local repository packages (Bash) - uses: actions/cache@v3 - if: ${{ runner.os != 'Windows' }} - with: - path: | - $HOME/.conan/data - $HOME/.conan/conan_download_cache - key: conan-${{ inputs.runs_on }}-${{ runner.arch }}-create-cache + - name: Cache Conan local repository packages (Bash) + uses: actions/cache@v3 + if: ${{ runner.os != 'Windows' }} + with: + path: | + $HOME/.conan/data + $HOME/.conan/conan_download_cache + key: conan-${{ inputs.runs_on }}-${{ runner.arch }}-create-cache - - name: Cache Conan local repository packages (Powershell) - uses: actions/cache@v3 - if: ${{ runner.os == 'Windows' }} - with: - path: | - C:\Users\runneradmin\.conan\data - C:\.conan - C:\Users\runneradmin\.conan\conan_download_cache - key: conan-${{ inputs.runs_on }}-${{ runner.arch }}-create-cache + - name: Cache Conan local repository packages (Powershell) + uses: actions/cache@v3 + if: ${{ runner.os == 'Windows' }} + with: + path: | + C:\Users\runneradmin\.conan\data + C:\.conan + C:\Users\runneradmin\.conan\conan_download_cache + key: conan-${{ inputs.runs_on }}-${{ runner.arch }}-create-cache - - name: Install MacOS system requirements - if: ${{ runner.os == 'Macos' }} - run: brew install autoconf automake ninja + - name: Install MacOS system requirements + if: ${{ runner.os == 'Macos' }} + run: brew install autoconf automake ninja - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config flex bison -y + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + if: ${{ runner.os == 'Linux' }} + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config flex bison -y - - name: Install GCC-12 on ubuntu-22.04 - if: ${{ startsWith(inputs.runs_on, 'ubuntu-22.04') }} - run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + - name: Install GCC-132 on ubuntu + if: ${{ startsWith(inputs.runs_on, 'ubuntu') }} + run: | + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 - - name: Use GCC-10 on ubuntu-20.04 - if: ${{ startsWith(inputs.runs_on, 'ubuntu-20.04') }} - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10 + - name: Create the default Conan profile + run: conan profile new default --detect - - name: Create the default Conan profile - run: conan profile new default --detect + - name: Get Conan configuration from branch + if: ${{ inputs.conan_config_branch != '' }} + run: conan config install https://github.com/Ultimaker/conan-config.git -a "-b ${{ inputs.conan_config_branch }}" - - name: Get Conan configuration from branch - if: ${{ inputs.conan_config_branch != '' }} - run: conan config install https://github.com/Ultimaker/conan-config.git -a "-b ${{ inputs.conan_config_branch }}" + - name: Get Conan configuration + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - name: Get Conan configuration - if: ${{ inputs.conan_config_branch == '' }} - run: conan config install https://github.com/Ultimaker/conan-config.git + - name: Add Cura private Artifactory remote + run: conan remote add cura-private https://ultimaker.jfrog.io/artifactory/api/conan/cura-private True - - name: Add Cura private Artifactory remote - run: conan remote add cura-private https://ultimaker.jfrog.io/artifactory/api/conan/cura-private True + - name: Create the Packages + run: conan install ${{ inputs.recipe_id_full }} --build=missing --update -c tools.build:skip_test=True - - name: Create the Packages - run: conan install ${{ inputs.recipe_id_full }} --build=missing --update -c tools.build:skip_test=True + - name: Upload the Package(s) + if: ${{ always() && inputs.conan_upload_community }} + run: conan upload ${{ inputs.recipe_id_full }} -r cura --all -c - - name: Upload the Package(s) - if: ${{ always() && inputs.conan_upload_community }} - run: conan upload ${{ inputs.recipe_id_full }} -r cura --all -c - - - name: Upload the Package(s) to the private Artifactory - if: ${{ always() && ! inputs.conan_upload_community }} - run: conan upload ${{ inputs.recipe_id_full }} -r cura-private --all -c + - name: Upload the Package(s) to the private Artifactory + if: ${{ always() && ! inputs.conan_upload_community }} + run: conan upload ${{ inputs.recipe_id_full }} -r cura-private --all -c diff --git a/.github/workflows/conan-package.yml b/.github/workflows/conan-package.yml index b5c135e23e..34652de39b 100644 --- a/.github/workflows/conan-package.yml +++ b/.github/workflows/conan-package.yml @@ -49,15 +49,15 @@ on: - '[1-9].[0-9][0-9].[0-9]*' env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: ${{ inputs.conan_logging_level }} - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} + CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} + CONAN_LOG_RUN_TO_OUTPUT: 1 + CONAN_LOGGING_LEVEL: ${{ inputs.conan_logging_level }} + CONAN_NON_INTERACTIVE: 1 -permissions: {} +permissions: { } jobs: conan-recipe-version: permissions: @@ -103,15 +103,20 @@ jobs: sudo apt update sudo apt upgrade sudo apt install efibootmgr build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config flex bison g++-12 gcc-12 -y - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + + - name: Install GCC-13 + run: | + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 - name: Create the default Conan profile run: conan profile new default --detect --force - name: Get Conan configuration - run: conan config install https://github.com/Ultimaker/conan-config.git + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - name: Create the Packages run: conan create . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} --build=missing --update -o ${{ needs.conan-recipe-version.outputs.project_name }}:devtools=True -c tools.build:skip_test=True diff --git a/.github/workflows/conan-recipe-export.yml b/.github/workflows/conan-recipe-export.yml index 4bd88a99c1..ba5aaa49a1 100644 --- a/.github/workflows/conan-recipe-export.yml +++ b/.github/workflows/conan-recipe-export.yml @@ -1,106 +1,107 @@ name: Export Conan Recipe to server on: - workflow_call: - inputs: - recipe_id_full: - required: true - type: string + workflow_call: + inputs: + recipe_id_full: + required: true + type: string - recipe_id_latest: - required: false - type: string + recipe_id_latest: + required: false + type: string - runs_on: - required: true - type: string + runs_on: + required: true + type: string - python_version: - required: true - type: string + python_version: + required: true + type: string - conan_config_branch: - required: false - type: string + conan_config_branch: + required: false + type: string - conan_logging_level: - required: false - type: string + conan_logging_level: + required: false + type: string - conan_export_binaries: - required: false - type: boolean + conan_export_binaries: + required: false + type: boolean - conan_upload_community: - required: false - default: true - type: boolean + conan_upload_community: + required: false + default: true + type: boolean env: - CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: ${{ inputs.conan_logging_level }} - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD: ${{ secrets.CONAN_PASS }} + CONAN_LOG_RUN_TO_OUTPUT: 1 + CONAN_LOGGING_LEVEL: ${{ inputs.conan_logging_level }} + CONAN_NON_INTERACTIVE: 1 jobs: - package-export: - runs-on: ${{ inputs.runs_on }} + package-export: + runs-on: ${{ inputs.runs_on }} - steps: - - name: Checkout project - uses: actions/checkout@v3 + steps: + - name: Checkout project + uses: actions/checkout@v3 - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: ${{ inputs.python_version }} - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt + - name: Setup Python and pip + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python_version }} + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-conan-package.txt - - name: Install Python requirements and Create default Conan profile - run: | - pip install -r https://raw.githubusercontent.com/Ultimaker/Cura/main/.github/workflows/requirements-conan-package.txt - conan profile new default --detect - # Note the runner requirements are always installed from the main branch in the Ultimaker/Cura repo + - name: Install Python requirements and Create default Conan profile + run: | + pip install -r https://raw.githubusercontent.com/Ultimaker/Cura/main/.github/workflows/requirements-conan-package.txt + conan profile new default --detect + # Note the runner requirements are always installed from the main branch in the Ultimaker/Cura repo - - name: Cache Conan local repository packages - uses: actions/cache@v3 - with: - path: $HOME/.conan/data - key: ${{ runner.os }}-conan-export-cache + - name: Cache Conan local repository packages + uses: actions/cache@v3 + with: + path: $HOME/.conan/data + key: ${{ runner.os }}-conan-export-cache - - name: Get Conan configuration from branch - if: ${{ inputs.conan_config_branch != '' }} - run: conan config install https://github.com/Ultimaker/conan-config.git -a "-b ${{ inputs.conan_config_branch }}" + - name: Get Conan configuration from branch + if: ${{ inputs.conan_config_branch != '' }} + run: conan config install https://github.com/Ultimaker/conan-config.git -a "-b ${{ inputs.conan_config_branch }}" - - name: Get Conan configuration - if: ${{ inputs.conan_config_branch == '' }} - run: conan config install https://github.com/Ultimaker/conan-config.git + - name: Get Conan configuration + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - name: Add Cura private Artifactory remote - run: conan remote add cura-private https://ultimaker.jfrog.io/artifactory/api/conan/cura-private True + - name: Add Cura private Artifactory remote + run: conan remote add cura-private https://ultimaker.jfrog.io/artifactory/api/conan/cura-private True - - name: Export the Package (binaries) - if: ${{ inputs.conan_export_binaries }} - run: conan create . ${{ inputs.recipe_id_full }} --build=missing --update -c tools.build:skip_test=True + - name: Export the Package (binaries) + if: ${{ inputs.conan_export_binaries }} + run: conan create . ${{ inputs.recipe_id_full }} --build=missing --update -c tools.build:skip_test=True - - name: Export the Package - if: ${{ !inputs.conan_export_binaries }} - run: conan export . ${{ inputs.recipe_id_full }} + - name: Export the Package + if: ${{ !inputs.conan_export_binaries }} + run: conan export . ${{ inputs.recipe_id_full }} - - name: Create the latest alias - if: always() - run: conan alias ${{ inputs.recipe_id_latest }} ${{ inputs.recipe_id_full }} + - name: Create the latest alias + if: always() + run: conan alias ${{ inputs.recipe_id_latest }} ${{ inputs.recipe_id_full }} - - name: Upload the Package(s) - if: ${{ always() && inputs.conan_upload_community }} - run: | - conan upload ${{ inputs.recipe_id_full }} -r cura --all -c - conan upload ${{ inputs.recipe_id_latest }} -r cura -c + - name: Upload the Package(s) + if: ${{ always() && inputs.conan_upload_community }} + run: | + conan upload ${{ inputs.recipe_id_full }} -r cura --all -c + conan upload ${{ inputs.recipe_id_latest }} -r cura -c - - name: Upload the Package(s) to the private Artifactory - if: ${{ always() && ! inputs.conan_upload_community }} - run: | - conan upload ${{ inputs.recipe_id_full }} -r cura-private --all -c - conan upload ${{ inputs.recipe_id_latest }} -r cura-private -c + - name: Upload the Package(s) to the private Artifactory + if: ${{ always() && ! inputs.conan_upload_community }} + run: | + conan upload ${{ inputs.recipe_id_full }} -r cura-private --all -c + conan upload ${{ inputs.recipe_id_latest }} -r cura-private -c diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 5a18199e3c..2e15584299 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -2,270 +2,263 @@ name: Linux Installer run-name: ${{ inputs.cura_conan_version }} for Linux-${{ inputs.architecture }} by @${{ github.actor }} on: - workflow_dispatch: - inputs: - cura_conan_version: - description: 'Cura Conan Version' - default: 'cura/latest@ultimaker/testing' - required: true - type: string - conan_args: - description: 'Conan args: eq.: --require-override' - default: '' - required: false - type: string - enterprise: - description: 'Build Cura as an Enterprise edition' - default: false - required: true - type: boolean - staging: - description: 'Use staging API' - default: false - required: true - type: boolean - architecture: - description: 'Architecture' - required: true - default: 'X64' - type: choice - options: - - X64 - operating_system: - description: 'OS' - required: true - default: 'ubuntu-22.04' - type: choice - options: - - ubuntu-22.04 - - ubuntu-20.04 - workflow_call: - inputs: - cura_conan_version: - description: 'Cura Conan Version' - default: 'cura/latest@ultimaker/testing' - required: true - type: string - conan_args: - description: 'Conan args: eq.: --require-override' - default: '' - required: false - type: string - enterprise: - description: 'Build Cura as an Enterprise edition' - default: false - required: true - type: boolean - staging: - description: 'Use staging API' - default: false - required: true - type: boolean - architecture: - description: 'Architecture' - required: true - default: 'X64' - type: string - operating_system: - description: 'OS' - required: true - default: 'ubuntu-22.04' - type: string + workflow_dispatch: + inputs: + cura_conan_version: + description: 'Cura Conan Version' + default: 'cura/latest@ultimaker/testing' + required: true + type: string + conan_args: + description: 'Conan args: eq.: --require-override' + default: '' + required: false + type: string + enterprise: + description: 'Build Cura as an Enterprise edition' + default: false + required: true + type: boolean + staging: + description: 'Use staging API' + default: false + required: true + type: boolean + architecture: + description: 'Architecture' + required: true + default: 'X64' + type: choice + options: + - X64 + operating_system: + description: 'OS' + required: true + default: 'ubuntu-22.04' + type: choice + options: + - ubuntu-22.04 + - ubuntu-20.04 + workflow_call: + inputs: + cura_conan_version: + description: 'Cura Conan Version' + default: 'cura/latest@ultimaker/testing' + required: true + type: string + conan_args: + description: 'Conan args: eq.: --require-override' + default: '' + required: false + type: string + enterprise: + description: 'Build Cura as an Enterprise edition' + default: false + required: true + type: boolean + staging: + description: 'Use staging API' + default: false + required: true + type: boolean + architecture: + description: 'Architecture' + required: true + default: 'X64' + type: string + operating_system: + description: 'OS' + required: true + default: 'ubuntu-22.04' + type: string env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} - CURA_CONAN_VERSION: ${{ inputs.cura_conan_version }} - ENTERPRISE: ${{ inputs.enterprise }} - STAGING: ${{ inputs.staging }} + CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} + CURA_CONAN_VERSION: ${{ inputs.cura_conan_version }} + ENTERPRISE: ${{ inputs.enterprise }} + STAGING: ${{ inputs.staging }} jobs: - cura-installer-create: - runs-on: ${{ inputs.operating_system }} + cura-installer-create: + runs-on: ${{ inputs.operating_system }} - steps: - - name: Checkout - uses: actions/checkout@v3 + steps: + - name: Checkout + uses: actions/checkout@v3 - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: '3.10.x' - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt - - - name: Install Python requirements for runner - run: pip install -r .github/workflows/requirements-conan-package.txt - - - name: Cache Conan local repository packages (Bash) - uses: actions/cache@v3 - with: - path: | - $HOME/.conan/data - $HOME/.conan/conan_download_cache - key: conan-${{ runner.os }}-${{ runner.arch }}-installer-cache - - - name: Hack needed specifically for ubuntu-22.04 from mid-Feb 2023 onwards - if: ${{ startsWith(inputs.operating_system, 'ubuntu-22.04') }} - run: sudo apt remove libodbc2 libodbcinst2 unixodbc-common -y - - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - wget --no-check-certificate --quiet https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O $GITHUB_WORKSPACE/appimagetool - chmod +x $GITHUB_WORKSPACE/appimagetool - echo "APPIMAGETOOL_LOCATION=$GITHUB_WORKSPACE/appimagetool" >> $GITHUB_ENV - - - name: Install GCC-12 on ubuntu-22.04 - if: ${{ startsWith(inputs.operating_system, 'ubuntu-22.04') }} - run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 - - - name: Use GCC-10 on ubuntu-20.04 - if: ${{ startsWith(inputs.operating_system, 'ubuntu-20.04') }} - run: | - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 10 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 10 - - - name: Create the default Conan profile - run: conan profile new default --detect --force - - - name: Configure GPG Key Linux (Bash) - run: echo -n "$GPG_PRIVATE_KEY" | base64 --decode | gpg --import - - - name: Get Conan configuration - run: | - conan config install https://github.com/Ultimaker/conan-config.git - conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - - name: Use Conan download cache (Bash) - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - - name: Create the Packages (Bash) - run: conan install $CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$ENTERPRISE -o cura:staging=$STAGING --json "cura_inst/conan_install_info.json" - - - name: Upload the Package(s) - if: always() - run: | - conan upload "*" -r cura --all -c - - - name: Set Environment variables for Cura (bash) - run: | - . ./cura_inst/bin/activate_github_actions_env.sh - . ./cura_inst/bin/activate_github_actions_version_env.sh - - # FIXME: This is a workaround to ensure that we use and pack a shared library for OpenSSL 1.1.1l. We currently compile - # OpenSSL statically for CPython, but our Python Dependenies (such as PyQt6) require a shared library. - # Because Conan won't allow for building the same library with two different options (easily) we need to install it explicitly - # and do a manual copy to the VirtualEnv, such that Pyinstaller can find it. - - - name: Install OpenSSL shared - run: conan install openssl/1.1.1l@_/_ --build=missing --update -o openssl:shared=True -g deploy - - - name: Copy OpenSSL shared (Bash) - run: | - cp ./openssl/lib/*.so* ./cura_inst/bin/ || true - cp ./openssl/lib/*.dylib* ./cura_inst/bin/ || true - - - name: Create the Cura dist - run: pyinstaller ./cura_inst/UltiMaker-Cura.spec - - - name: Output the name file name and extension - id: filename - shell: python - run: | - import os - enterprise = "-Enterprise" if "${{ inputs.enterprise }}" == "true" else "" - if "${{ inputs.operating_system }}" == "ubuntu-22.04": - installer_filename = f"UltiMaker-Cura-{os.getenv('CURA_VERSION_FULL')}{enterprise}-linux-modern-${{ inputs.architecture }}" - else: - installer_filename = f"UltiMaker-Cura-{os.getenv('CURA_VERSION_FULL')}{enterprise}-linux-${{ inputs.architecture }}" - output_env = os.environ["GITHUB_OUTPUT"] - content = "" - if os.path.exists(output_env): - with open(output_env, "r") as f: - content = f.read() - with open(output_env, "w") as f: - f.write(content) - f.writelines(f"INSTALLER_FILENAME={installer_filename}\n") - - - name: Summarize the used Conan dependencies - shell: python - run: | - import os - import json - from pathlib import Path - - conan_install_info_path = Path("cura_inst/conan_install_info.json") - conan_info = {"installed": []} - if os.path.exists(conan_install_info_path): - with open(conan_install_info_path, "r") as f: - conan_info = json.load(f) - sorted_deps = sorted([dep["recipe"]["id"].replace('#', r' rev: ') for dep in conan_info["installed"]]) - - summary_env = os.environ["GITHUB_STEP_SUMMARY"] - content = "" - if os.path.exists(summary_env): - with open(summary_env, "r") as f: - content = f.read() - - with open(summary_env, "w") as f: - f.write(content) - f.writelines("# ${{ steps.filename.outputs.INSTALLER_FILENAME }}\n") - f.writelines("## Conan packages:\n") - for dep in sorted_deps: - f.writelines(f"`{dep}`\n") - - - name: Summarize the used Python modules - shell: python - run: | - import os - import pkg_resources - summary_env = os.environ["GITHUB_STEP_SUMMARY"] - content = "" - if os.path.exists(summary_env): - with open(summary_env, "r") as f: - content = f.read() - - with open(summary_env, "w") as f: - f.write(content) - f.writelines("## Python modules:\n") - for package in pkg_resources.working_set: - f.writelines(f"`{package.key}/{package.version}`\n") - - - name: Create the Linux AppImage (Bash) - run: | - python ../cura_inst/packaging/AppImage/create_appimage.py ./UltiMaker-Cura $CURA_VERSION_FULL "${{ steps.filename.outputs.INSTALLER_FILENAME }}.AppImage" - chmod +x "${{ steps.filename.outputs.INSTALLER_FILENAME }}.AppImage" - working-directory: dist - - - name: Upload the AppImage - uses: actions/upload-artifact@v3 - with: - name: ${{ steps.filename.outputs.INSTALLER_FILENAME }}-AppImage - path: | - dist/${{ steps.filename.outputs.INSTALLER_FILENAME }}.AppImage - retention-days: 5 - - notify-export: - if: ${{ always() }} - needs: [ cura-installer-create ] - - uses: ultimaker/cura/.github/workflows/notify.yml@main + - name: Setup Python and pip + uses: actions/setup-python@v4 with: - success: ${{ contains(join(needs.*.result, ','), 'success') }} - success_title: "Create the Cura distributions" - success_body: "Installers for ${{ inputs.cura_conan_version }}" - failure_title: "Failed to create the Cura distributions" - failure_body: "Failed to create at least 1 installer for ${{ inputs.cura_conan_version }}" - secrets: inherit + python-version: '3.10.x' + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-conan-package.txt + + - name: Install Python requirements for runner + run: pip install -r .github/workflows/requirements-conan-package.txt + + - name: Cache Conan local repository packages (Bash) + uses: actions/cache@v3 + with: + path: | + $HOME/.conan/data + $HOME/.conan/conan_download_cache + key: conan-${{ runner.os }}-${{ runner.arch }}-installer-cache + + - name: Hack needed specifically for ubuntu-22.04 from mid-Feb 2023 onwards + if: ${{ startsWith(inputs.operating_system, 'ubuntu-22.04') }} + run: sudo apt remove libodbc2 libodbcinst2 unixodbc-common -y + + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y + wget --no-check-certificate --quiet https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O $GITHUB_WORKSPACE/appimagetool + chmod +x $GITHUB_WORKSPACE/appimagetool + echo "APPIMAGETOOL_LOCATION=$GITHUB_WORKSPACE/appimagetool" >> $GITHUB_ENV + + - name: Install GCC-13 + run: | + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 + + - name: Create the default Conan profile + run: conan profile new default --detect --force + + - name: Configure GPG Key Linux (Bash) + run: echo -n "$GPG_PRIVATE_KEY" | base64 --decode | gpg --import + + - name: Get Conan configuration + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" + + - name: Use Conan download cache (Bash) + run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" + + - name: Create the Packages (Bash) + run: conan install $CURA_CONAN_VERSION ${{ inputs.conan_args }} --build=missing --update -if cura_inst -g VirtualPythonEnv -o cura:enterprise=$ENTERPRISE -o cura:staging=$STAGING --json "cura_inst/conan_install_info.json" + + - name: Upload the Package(s) + if: always() + run: | + conan upload "*" -r cura --all -c + + - name: Set Environment variables for Cura (bash) + run: | + . ./cura_inst/bin/activate_github_actions_env.sh + . ./cura_inst/bin/activate_github_actions_version_env.sh + + # FIXME: This is a workaround to ensure that we use and pack a shared library for OpenSSL 1.1.1l. We currently compile + # OpenSSL statically for CPython, but our Python Dependenies (such as PyQt6) require a shared library. + # Because Conan won't allow for building the same library with two different options (easily) we need to install it explicitly + # and do a manual copy to the VirtualEnv, such that Pyinstaller can find it. + + - name: Install OpenSSL shared + run: conan install openssl/1.1.1l@_/_ --build=missing --update -o openssl:shared=True -g deploy + + - name: Copy OpenSSL shared (Bash) + run: | + cp ./openssl/lib/*.so* ./cura_inst/bin/ || true + cp ./openssl/lib/*.dylib* ./cura_inst/bin/ || true + + - name: Create the Cura dist + run: pyinstaller ./cura_inst/UltiMaker-Cura.spec + + - name: Output the name file name and extension + id: filename + shell: python + run: | + import os + enterprise = "-Enterprise" if "${{ inputs.enterprise }}" == "true" else "" + if "${{ inputs.operating_system }}" == "ubuntu-22.04": + installer_filename = f"UltiMaker-Cura-{os.getenv('CURA_VERSION_FULL')}{enterprise}-linux-modern-${{ inputs.architecture }}" + else: + installer_filename = f"UltiMaker-Cura-{os.getenv('CURA_VERSION_FULL')}{enterprise}-linux-${{ inputs.architecture }}" + output_env = os.environ["GITHUB_OUTPUT"] + content = "" + if os.path.exists(output_env): + with open(output_env, "r") as f: + content = f.read() + with open(output_env, "w") as f: + f.write(content) + f.writelines(f"INSTALLER_FILENAME={installer_filename}\n") + + - name: Summarize the used Conan dependencies + shell: python + run: | + import os + import json + from pathlib import Path + + conan_install_info_path = Path("cura_inst/conan_install_info.json") + conan_info = {"installed": []} + if os.path.exists(conan_install_info_path): + with open(conan_install_info_path, "r") as f: + conan_info = json.load(f) + sorted_deps = sorted([dep["recipe"]["id"].replace('#', r' rev: ') for dep in conan_info["installed"]]) + + summary_env = os.environ["GITHUB_STEP_SUMMARY"] + content = "" + if os.path.exists(summary_env): + with open(summary_env, "r") as f: + content = f.read() + + with open(summary_env, "w") as f: + f.write(content) + f.writelines("# ${{ steps.filename.outputs.INSTALLER_FILENAME }}\n") + f.writelines("## Conan packages:\n") + for dep in sorted_deps: + f.writelines(f"`{dep}`\n") + + - name: Summarize the used Python modules + shell: python + run: | + import os + import pkg_resources + summary_env = os.environ["GITHUB_STEP_SUMMARY"] + content = "" + if os.path.exists(summary_env): + with open(summary_env, "r") as f: + content = f.read() + + with open(summary_env, "w") as f: + f.write(content) + f.writelines("## Python modules:\n") + for package in pkg_resources.working_set: + f.writelines(f"`{package.key}/{package.version}`\n") + + - name: Create the Linux AppImage (Bash) + run: | + python ../cura_inst/packaging/AppImage/create_appimage.py ./UltiMaker-Cura $CURA_VERSION_FULL "${{ steps.filename.outputs.INSTALLER_FILENAME }}.AppImage" + chmod +x "${{ steps.filename.outputs.INSTALLER_FILENAME }}.AppImage" + working-directory: dist + + - name: Upload the AppImage + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.filename.outputs.INSTALLER_FILENAME }}-AppImage + path: | + dist/${{ steps.filename.outputs.INSTALLER_FILENAME }}.AppImage + retention-days: 5 + + notify-export: + if: ${{ always() }} + needs: [ cura-installer-create ] + + uses: ultimaker/cura/.github/workflows/notify.yml@main + with: + success: ${{ contains(join(needs.*.result, ','), 'success') }} + success_title: "Create the Cura distributions" + success_body: "Installers for ${{ inputs.cura_conan_version }}" + failure_title: "Failed to create the Cura distributions" + failure_body: "Failed to create at least 1 installer for ${{ inputs.cura_conan_version }}" + secrets: inherit diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml index f08acbdb04..8321f42a23 100644 --- a/.github/workflows/unit-test.yml +++ b/.github/workflows/unit-test.yml @@ -2,163 +2,165 @@ name: unit-test on: - push: - paths: - - 'plugins/**' - - 'resources/**' - - 'cura/**' - - 'icons/**' - - 'tests/**' - - 'packaging/**' - - '.github/workflows/conan-*.yml' - - '.github/workflows/unit-test.yml' - - '.github/workflows/notify.yml' - - '.github/workflows/requirements-conan-package.txt' - - 'requirements*.txt' - - 'conanfile.py' - - 'conandata.yml' - - 'GitVersion.yml' - - '*.jinja' - branches: - - main - - 'CURA-*' - - '[1-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' - - '[0-9]+.[0-9]+-beta' - pull_request: - paths: - - 'plugins/**' - - 'resources/**' - - 'cura/**' - - 'icons/**' - - 'tests/**' - - 'packaging/**' - - '.github/workflows/conan-*.yml' - - '.github/workflows/unit-test.yml' - - '.github/workflows/notify.yml' - - '.github/workflows/requirements-conan-package.txt' - - 'requirements*.txt' - - 'conanfile.py' - - 'conandata.yml' - - 'GitVersion.yml' - - '*.jinja' - branches: - - main - - '[1-9]+.[0-9]+' - tags: - - '[0-9]+.[0-9]+.[0-9]+' - - '[0-9]+.[0-9]+-beta' + push: + paths: + - 'plugins/**' + - 'resources/**' + - 'cura/**' + - 'icons/**' + - 'tests/**' + - 'packaging/**' + - '.github/workflows/conan-*.yml' + - '.github/workflows/unit-test.yml' + - '.github/workflows/notify.yml' + - '.github/workflows/requirements-conan-package.txt' + - 'requirements*.txt' + - 'conanfile.py' + - 'conandata.yml' + - 'GitVersion.yml' + - '*.jinja' + branches: + - main + - 'CURA-*' + - '[1-9]+.[0-9]+' + tags: + - '[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+-beta' + pull_request: + paths: + - 'plugins/**' + - 'resources/**' + - 'cura/**' + - 'icons/**' + - 'tests/**' + - 'packaging/**' + - '.github/workflows/conan-*.yml' + - '.github/workflows/unit-test.yml' + - '.github/workflows/notify.yml' + - '.github/workflows/requirements-conan-package.txt' + - 'requirements*.txt' + - 'conanfile.py' + - 'conandata.yml' + - 'GitVersion.yml' + - '*.jinja' + branches: + - main + - '[1-9]+.[0-9]+' + tags: + - '[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+-beta' env: - CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} - CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} - CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} - CONAN_LOG_RUN_TO_OUTPUT: 1 - CONAN_LOGGING_LEVEL: info - CONAN_NON_INTERACTIVE: 1 + CONAN_LOGIN_USERNAME_CURA: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA: ${{ secrets.CONAN_PASS }} + CONAN_LOGIN_USERNAME_CURA_CE: ${{ secrets.CONAN_USER }} + CONAN_PASSWORD_CURA_CE: ${{ secrets.CONAN_PASS }} + CONAN_LOG_RUN_TO_OUTPUT: 1 + CONAN_LOGGING_LEVEL: info + CONAN_NON_INTERACTIVE: 1 permissions: contents: read jobs: - conan-recipe-version: - uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + conan-recipe-version: + uses: ultimaker/cura/.github/workflows/conan-recipe-version.yml@main + with: + project_name: cura + + testing: + runs-on: ubuntu-22.04 + needs: [ conan-recipe-version ] + + steps: + - name: Checkout + uses: actions/checkout@v3 with: - project_name: cura + fetch-depth: 2 - testing: - runs-on: ubuntu-22.04 - needs: [ conan-recipe-version ] + - name: Setup Python and pip + uses: actions/setup-python@v4 + with: + python-version: '3.11.x' + architecture: 'x64' + cache: 'pip' + cache-dependency-path: .github/workflows/requirements-conan-package.txt - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 2 + - name: Install Python requirements and Create default Conan profile + run: pip install -r requirements-conan-package.txt + working-directory: .github/workflows/ - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: '3.11.x' - architecture: 'x64' - cache: 'pip' - cache-dependency-path: .github/workflows/requirements-conan-package.txt + - name: Use Conan download cache (Bash) + if: ${{ runner.os != 'Windows' }} + run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" - - name: Install Python requirements and Create default Conan profile - run: pip install -r requirements-conan-package.txt - working-directory: .github/workflows/ + - name: Cache Conan local repository packages (Bash) + uses: actions/cache@v3 + if: ${{ runner.os != 'Windows' }} + with: + path: | + $HOME/.conan/data + $HOME/.conan/conan_download_cache + key: conan-${{ runner.os }}-${{ runner.arch }}-unit-cache - - name: Use Conan download cache (Bash) - if: ${{ runner.os != 'Windows' }} - run: conan config set storage.download_cache="$HOME/.conan/conan_download_cache" + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + if: ${{ runner.os == 'Linux' }} + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y - - name: Cache Conan local repository packages (Bash) - uses: actions/cache@v3 - if: ${{ runner.os != 'Windows' }} - with: - path: | - $HOME/.conan/data - $HOME/.conan/conan_download_cache - key: conan-${{ runner.os }}-${{ runner.arch }}-unit-cache + - name: Install GCC-13 + run: | + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config -y + - name: Get Conan configuration + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - name: Install GCC-12 on ubuntu-22.04 - run: | - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 - - - name: Get Conan configuration - run: conan config install https://github.com/Ultimaker/conan-config.git + - name: Get Conan profile + run: conan profile new default --detect --force - - name: Get Conan profile - run: conan profile new default --detect --force + - name: Install dependencies + run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} --build=missing --update -o cura:devtools=True -g VirtualPythonEnv -if venv - - name: Install dependencies - run: conan install . ${{ needs.conan-recipe-version.outputs.recipe_id_full }} --build=missing --update -o cura:devtools=True -g VirtualPythonEnv -if venv + - name: Upload the Dependency package(s) + run: conan upload "*" -r cura --all -c - - name: Upload the Dependency package(s) - run: conan upload "*" -r cura --all -c + - name: Set Environment variables for Cura (bash) + if: ${{ runner.os != 'Windows' }} + run: | + . ./venv/bin/activate_github_actions_env.sh - - name: Set Environment variables for Cura (bash) - if: ${{ runner.os != 'Windows' }} - run: | - . ./venv/bin/activate_github_actions_env.sh + - name: Run Unit Test + id: run-test + run: | + pytest --junitxml=junit_cura.xml + working-directory: tests - - name: Run Unit Test - id: run-test - run: | - pytest --junitxml=junit_cura.xml - working-directory: tests + - name: Save PR metadata + if: always() + run: | + echo ${{ github.event.number }} > pr-id.txt + echo ${{ github.event.pull_request.head.repo.full_name }} > pr-head-repo.txt + echo ${{ github.event.pull_request.head.ref }} > pr-head-ref.txt + working-directory: tests - - name: Save PR metadata - if: always() - run: | - echo ${{ github.event.number }} > pr-id.txt - echo ${{ github.event.pull_request.head.repo.full_name }} > pr-head-repo.txt - echo ${{ github.event.pull_request.head.ref }} > pr-head-ref.txt - working-directory: tests - - - name: Upload Test Results - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-result - path: | - tests/**/*.xml - tests/pr-id.txt - tests/pr-head-repo.txt - tests/pr-head-ref.txt + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-result + path: | + tests/**/*.xml + tests/pr-id.txt + tests/pr-head-repo.txt + tests/pr-head-ref.txt diff --git a/.github/workflows/update-translation.yml b/.github/workflows/update-translation.yml index 65693be937..55ce144666 100644 --- a/.github/workflows/update-translation.yml +++ b/.github/workflows/update-translation.yml @@ -1,82 +1,87 @@ name: update-translations on: - push: - paths: - - 'plugins/**' - - 'resources/**' - - 'cura/**' - - 'icons/**' - - 'tests/**' - - 'packaging/**' - - '.github/workflows/conan-*.yml' - - '.github/workflows/notify.yml' - - '.github/workflows/requirements-conan-package.txt' - - 'requirements*.txt' - - 'conanfile.py' - - 'conandata.yml' - - 'GitVersion.yml' - - '*.jinja' - branches: - - '[1-9].[0-9]' - - '[1-9].[0-9][0-9]' - tags: - - '[1-9].[0-9].[0-9]*' - - '[1-9].[0-9].[0-9]' - - '[1-9].[0-9][0-9].[0-9]*' + push: + paths: + - 'plugins/**' + - 'resources/**' + - 'cura/**' + - 'icons/**' + - 'tests/**' + - 'packaging/**' + - '.github/workflows/conan-*.yml' + - '.github/workflows/notify.yml' + - '.github/workflows/requirements-conan-package.txt' + - 'requirements*.txt' + - 'conanfile.py' + - 'conandata.yml' + - 'GitVersion.yml' + - '*.jinja' + branches: + - '[1-9].[0-9]' + - '[1-9].[0-9][0-9]' + tags: + - '[1-9].[0-9].[0-9]*' + - '[1-9].[0-9].[0-9]' + - '[1-9].[0-9][0-9].[0-9]*' jobs: - update-translations: - name: Update translations + update-translations: + name: Update translations - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 - - name: Cache Conan data - id: cache-conan - uses: actions/cache@v3 - with: - path: ~/.conan - key: ${{ runner.os }}-conan + - name: Cache Conan data + id: cache-conan + uses: actions/cache@v3 + with: + path: ~/.conan + key: ${{ runner.os }}-conan - - name: Setup Python and pip - uses: actions/setup-python@v4 - with: - python-version: 3.10.x - cache: pip - cache-dependency-path: .github/workflows/requirements-conan-package.txt + - name: Setup Python and pip + uses: actions/setup-python@v4 + with: + python-version: 3.11.x + cache: pip + cache-dependency-path: .github/workflows/requirements-conan-package.txt - - name: Install Python requirements for runner - run: pip install -r .github/workflows/requirements-conan-package.txt + - name: Install Python requirements for runner + run: pip install -r .github/workflows/requirements-conan-package.txt - # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. - # This is maybe because grub caches the disk it uses last time, which is recreated each time. - - name: Install Linux system requirements - if: ${{ runner.os == 'Linux' }} - run: | - sudo rm /var/cache/debconf/config.dat - sudo dpkg --configure -a - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt update - sudo apt upgrade - sudo apt install efibootmgr build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config flex bison g++-12 gcc-12 -y - sudo apt install g++-12 gcc-12 -y - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-12 12 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12 + # NOTE: Due to what are probably github issues, we have to remove the cache and reconfigure before the rest. + # This is maybe because grub caches the disk it uses last time, which is recreated each time. + - name: Install Linux system requirements + if: ${{ runner.os == 'Linux' }} + run: | + sudo rm /var/cache/debconf/config.dat + sudo dpkg --configure -a + sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt update + sudo apt upgrade + sudo apt install efibootmgr build-essential checkinstall libegl-dev zlib1g-dev libssl-dev ninja-build autoconf libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxau-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxdmcp-dev libxext-dev libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev libxss-dev libxt-dev libxtst-dev libxv-dev libxvmc-dev libxxf86vm-dev xtrans-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev xkb-data libxcb-dri3-dev uuid-dev libxcb-util-dev libxkbcommon-x11-dev pkg-config flex bison g++-12 gcc-12 -y - - name: Create the default Conan profile - run: conan profile new default --detect --force + - name: Install GCC-13 + run: | + sudo apt install g++-13 gcc-13 -y + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 13 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-13 13 - - name: Get Conan configuration - run: conan config install https://github.com/Ultimaker/conan-config.git + - name: Create the default Conan profile + run: conan profile new default --detect --force - - name: generate the files using Conan install - run: conan install . --build=missing --update -o cura:devtools=True + - name: Get Conan configuration + run: | + conan config install https://github.com/Ultimaker/conan-config.git + conan config install https://github.com/Ultimaker/conan-config.git -a "-b runner/${{ runner.os }}/${{ runner.arch }}" - - uses: stefanzweifel/git-auto-commit-action@v4 - with: - file_pattern: resources/i18n/*.po resources/i18n/*.pot - status_options: --untracked-files=no - commit_message: update translations + - name: generate the files using Conan install + run: conan install . --build=missing --update -o cura:devtools=True + + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + file_pattern: resources/i18n/*.po resources/i18n/*.pot + status_options: --untracked-files=no + commit_message: update translations