mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-06 22:47:29 -06:00
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:
parent
b91ebcbb36
commit
b62725b4f0
7 changed files with 47 additions and 36 deletions
|
@ -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.")
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue