copy_paste in a grid

arrange all objects in grid
place in grid

Co-authored-by: Casper Lamboo <c.lamboo@ultimaker.com>

CURA-7951
This commit is contained in:
saumya.jain 2023-08-22 10:30:51 +02:00
parent b91ebcbb36
commit b62725b4f0
7 changed files with 47 additions and 36 deletions

View file

@ -8,6 +8,7 @@ from UM.Logger import Logger
from UM.Message import Message from UM.Message import Message
from UM.Scene.SceneNode import SceneNode from UM.Scene.SceneNode import SceneNode
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from cura.Arranging.GridArrange import GridArrange
from cura.Arranging.Nest2DArrange import arrange from cura.Arranging.Nest2DArrange import arrange
i18n_catalog = i18nCatalog("cura") i18n_catalog = i18nCatalog("cura")
@ -15,12 +16,12 @@ i18n_catalog = i18nCatalog("cura")
class ArrangeObjectsJob(Job): 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,
lock_rotation: bool = False) -> None: grid_arrange: bool = False) -> None:
super().__init__() super().__init__()
self._nodes = nodes self._nodes = nodes
self._fixed_nodes = fixed_nodes self._fixed_nodes = fixed_nodes
self._min_offset = min_offset self._min_offset = min_offset
self._lock_rotation = lock_rotation self._grid_arrange = grid_arrange
def run(self): def run(self):
found_solution_for_all = False found_solution_for_all = False
@ -32,8 +33,14 @@ class ArrangeObjectsJob(Job):
status_message.show() status_message.show()
try: 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 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.")

View file

@ -26,7 +26,13 @@ class GridArrange:
self._build_volume_bounding_box = build_volume.getBoundingBox() self._build_volume_bounding_box = build_volume.getBoundingBox()
self._fixed_nodes = fixed_nodes 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_width = 0
self._grid_height = 0 self._grid_height = 0
for node in self._nodes_to_arrange: for node in self._nodes_to_arrange:

View file

@ -18,6 +18,7 @@ from UM.Operations.RemoveSceneNodeOperation import RemoveSceneNodeOperation
from UM.Operations.TranslateOperation import TranslateOperation from UM.Operations.TranslateOperation import TranslateOperation
import cura.CuraApplication import cura.CuraApplication
from cura.Arranging.GridArrange import GridArrange
from cura.Operations.SetParentOperation import SetParentOperation from cura.Operations.SetParentOperation import SetParentOperation
from cura.MultiplyObjectsJob import MultiplyObjectsJob from cura.MultiplyObjectsJob import MultiplyObjectsJob
from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation from cura.Settings.SetObjectExtruderOperation import SetObjectExtruderOperation
@ -84,16 +85,16 @@ class CuraActions(QObject):
operation.push() operation.push()
@pyqtSlot(int, bool) @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 """Multiply all objects in the selection
:param count: The number of times to multiply 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 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) grid_arrange=grid_placement)
job.start() job.start()
@pyqtSlot() @pyqtSlot()
@ -231,9 +232,8 @@ class CuraActions(QObject):
if node.callDecoration("isSliceable"): if node.callDecoration("isSliceable"):
fixed_nodes.append(node) fixed_nodes.append(node)
# Add the new nodes to the scene, and arrange them # Add the new nodes to the scene, and arrange them
group_operation, not_fit_count = createGroupOperationForArrange(nodes, application.getBuildVolume(), grid_arrange = GridArrange(nodes, application.getBuildVolume(), fixed_nodes)
fixed_nodes, factor=10000, group_operation, not_fit_count = grid_arrange.createGroupOperationForArrange()
add_new_nodes_in_scene=True)
group_operation.push() group_operation.push()
# deselect currently selected nodes, and select the new nodes # deselect currently selected nodes, and select the new nodes

View file

@ -1441,7 +1441,7 @@ class CuraApplication(QtApplication):
# Single build plate # Single build plate
@pyqtSlot(bool) @pyqtSlot(bool)
def arrangeAll(self, lock_rotation: bool) -> None: def arrangeAll(self, grid_arrangement: bool) -> None:
nodes_to_arrange = [] nodes_to_arrange = []
active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate active_build_plate = self.getMultiBuildPlateModel().activeBuildPlate
locked_nodes = [] locked_nodes = []
@ -1471,18 +1471,18 @@ class CuraApplication(QtApplication):
locked_nodes.append(node) locked_nodes.append(node)
else: else:
nodes_to_arrange.append(node) 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 """Arrange a set of nodes given a set of fixed nodes
:param nodes: nodes that we have to place :param nodes: nodes that we have to place
:param fixed_nodes: nodes that are placed in the arranger before finding spots for nodes :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 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() job.start()
@pyqtSlot() @pyqtSlot()

View file

@ -21,12 +21,12 @@ i18n_catalog = i18nCatalog("cura")
class MultiplyObjectsJob(Job): 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__() super().__init__()
self._objects = objects self._objects = objects
self._count: int = count self._count: int = count
self._min_offset: int = min_offset self._min_offset: int = min_offset
self._lock_rotation: bool = lock_rotation self._grid_arrange: bool = grid_arrange
def run(self) -> None: def run(self) -> None:
status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime = 0, 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 found_solution_for_all = True
group_operation = GroupedOperation() group_operation = GroupedOperation()
if nodes: if nodes:
grid_arrange = GridArrange(nodes,Application.getInstance().getBuildVolume(), if(self._grid_arrange):
fixed_nodes) 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() else:
print("group_operation", group_operation) group_operation, not_fit_count = createGroupOperationForArrange(nodes,
# group_operation, not_fit_count = createGroupOperationForArrange(nodes, Application.getInstance().getBuildVolume(),
# Application.getInstance().getBuildVolume(), fixed_nodes,
# fixed_nodes, factor=10000,
# factor=10000, add_new_nodes_in_scene=True)
# 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: if nodes_to_add_without_arrange:
for nested_node in nodes_to_add_without_arrange: for nested_node in nodes_to_add_without_arrange:

View file

@ -41,7 +41,7 @@ Item
property alias deleteAll: deleteAllAction property alias deleteAll: deleteAllAction
property alias reloadAll: reloadAllAction property alias reloadAll: reloadAllAction
property alias arrangeAll: arrangeAllAction property alias arrangeAll: arrangeAllAction
property alias arrangeAllLock: arrangeAllLockAction property alias arrangeAllGrid: arrangeAllGridAction
property alias arrangeSelection: arrangeSelectionAction property alias arrangeSelection: arrangeSelectionAction
property alias arrangeSelectionLock: arrangeSelectionLockAction property alias arrangeSelectionLock: arrangeSelectionLockAction
property alias resetAllTranslation: resetAllTranslationAction property alias resetAllTranslation: resetAllTranslationAction
@ -464,8 +464,8 @@ Item
Action Action
{ {
id: arrangeAllLockAction id: arrangeAllGridAction
text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models Without Rotation") text: catalog.i18nc("@action:inmenu menubar:edit","Arrange All Models in a grid")
onTriggered: Printer.arrangeAll(true) onTriggered: Printer.arrangeAll(true)
shortcut: "Shift+Ctrl+R" shortcut: "Shift+Ctrl+R"
} }

View file

@ -66,7 +66,7 @@ Cura.Menu
Cura.MenuSeparator {} Cura.MenuSeparator {}
Cura.MenuItem { action: Cura.Actions.selectAll } Cura.MenuItem { action: Cura.Actions.selectAll }
Cura.MenuItem { action: Cura.Actions.arrangeAll } 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.deleteAll }
Cura.MenuItem { action: Cura.Actions.reloadAll } Cura.MenuItem { action: Cura.Actions.reloadAll }
Cura.MenuItem { action: Cura.Actions.resetAllTranslation } Cura.MenuItem { action: Cura.Actions.resetAllTranslation }
@ -110,7 +110,7 @@ Cura.Menu
minimumWidth: UM.Theme.getSize("small_popup_dialog").width minimumWidth: UM.Theme.getSize("small_popup_dialog").width
minimumHeight: UM.Theme.getSize("small_popup_dialog").height 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 buttonSpacing: UM.Theme.getSize("thin_margin").width
@ -158,8 +158,8 @@ Cura.Menu
UM.CheckBox UM.CheckBox
{ {
id: lockRotationField id: gridPlacementSelected
text: catalog.i18nc("@label", "Lock Rotation") text: catalog.i18nc("@label", "Grid Placement")
} }
} }
} }