def run(self): status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime = 0, dismissable=False, progress = 0, title = i18n_catalog.i18nc("@info:title", "Finding Location")) status_message.show() global_container_stack = Application.getInstance().getGlobalContainerStack() machine_width = global_container_stack.getProperty("machine_width", "value") machine_depth = global_container_stack.getProperty("machine_depth", "value") arranger = Arrange.create(x = machine_width, y = machine_depth, fixed_nodes = self._fixed_nodes, min_offset = self._min_offset) # Build set to exclude children (those get arranged together with the parents). included_as_child = set() for node in self._nodes: included_as_child.update(node.getAllChildren()) # Collect nodes to be placed nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr) for node in self._nodes: if node in included_as_child: continue offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset, include_children = True) if offset_shape_arr is None: Logger.log("w", "Node [%s] could not be converted to an array for arranging...", str(node)) continue nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr)) # Sort the nodes with the biggest area first. nodes_arr.sort(key=lambda item: item[0]) nodes_arr.reverse() # Place nodes one at a time start_priority = 0 last_priority = start_priority last_size = None grouped_operation = GroupedOperation() found_solution_for_all = True not_fit_count = 0 for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr): # For performance reasons, we assume that when a location does not fit, # it will also not fit for the next object (while what can be untrue). if last_size == size: # This optimization works if many of the objects have the same size start_priority = last_priority else: start_priority = 0 best_spot = arranger.bestSpot(hull_shape_arr, start_prio = start_priority) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): center_y = node.getWorldPosition().y - node.getBoundingBox().bottom else: center_y = 0 if x is not None: # We could find a place last_size = size last_priority = best_spot.priority arranger.place(x, y, offset_shape_arr) # take place before the next one grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) else: Logger.log("d", "Arrange all: could not find spot!") found_solution_for_all = False grouped_operation.addOperation(TranslateOperation(node, Vector(200, center_y, -not_fit_count * 20), set_position = True)) not_fit_count += 1 status_message.setProgress((idx + 1) / len(nodes_arr) * 100) Job.yieldThread() QCoreApplication.processEvents() grouped_operation.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"), title = i18n_catalog.i18nc("@info:title", "Can't Find Location")) no_full_solution_message.show() self.finished.emit(self)
def run(self): status_message = Message(i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime=0, dismissable=False, progress=0, title = i18n_catalog.i18nc("@info:title", "Placing Objects")) status_message.show() scene = Application.getInstance().getController().getScene() total_progress = len(self._objects) * self._count current_progress = 0 global_container_stack = Application.getInstance().getGlobalContainerStack() machine_width = global_container_stack.getProperty("machine_width", "value") machine_depth = global_container_stack.getProperty("machine_depth", "value") root = scene.getRoot() scale = 0.5 arranger = Arrange.create(x = machine_width, y = machine_depth, scene_root = root, scale = scale, min_offset = self._min_offset) processed_nodes = [] nodes = [] not_fit_count = 0 for node in self._objects: # If object is part of a group, multiply group current_node = node while current_node.getParent() and (current_node.getParent().callDecoration("isGroup") or current_node.getParent().callDecoration("isSliceable")): current_node = current_node.getParent() if current_node in processed_nodes: continue processed_nodes.append(current_node) node_too_big = False if node.getBoundingBox().width < machine_width or node.getBoundingBox().depth < machine_depth: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(current_node, min_offset = self._min_offset, scale = scale) else: node_too_big = True found_solution_for_all = True arranger.resetLastPriority() for i in range(self._count): # We do place the nodes one by one, as we want to yield in between. new_node = copy.deepcopy(node) solution_found = False if not node_too_big: solution_found = arranger.findNodePlacement(new_node, offset_shape_arr, hull_shape_arr) if node_too_big or not solution_found: found_solution_for_all = False new_location = new_node.getPosition() new_location = new_location.set(z = - not_fit_count * 20) new_node.setPosition(new_location) not_fit_count += 1 # Same build plate build_plate_number = current_node.callDecoration("getBuildPlateNumber") new_node.callDecoration("setBuildPlateNumber", build_plate_number) for child in new_node.getChildren(): child.callDecoration("setBuildPlateNumber", build_plate_number) nodes.append(new_node) current_progress += 1 status_message.setProgress((current_progress / total_progress) * 100) Job.yieldThread() Job.yieldThread() if nodes: op = GroupedOperation() for new_node in nodes: op.addOperation(AddSceneNodeOperation(new_node, current_node.getParent())) op.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"), title = i18n_catalog.i18nc("@info:title", "Placing Object")) no_full_solution_message.show()
def run(self): status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime = 0, dismissable=False, progress = 0, title = i18n_catalog.i18nc("@info:title", "Finding Location")) status_message.show() arranger = Arrange.create(fixed_nodes = self._fixed_nodes) # Collect nodes to be placed nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr) for node in self._nodes: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset) nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr)) # Sort the nodes with the biggest area first. nodes_arr.sort(key=lambda item: item[0]) nodes_arr.reverse() # Place nodes one at a time start_priority = 0 last_priority = start_priority last_size = None grouped_operation = GroupedOperation() found_solution_for_all = True for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr): # For performance reasons, we assume that when a location does not fit, # it will also not fit for the next object (while what can be untrue). # We also skip possibilities by slicing through the possibilities (step = 10) if last_size == size: # This optimization works if many of the objects have the same size start_priority = last_priority else: start_priority = 0 best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority, step=10) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): center_y = node.getWorldPosition().y - node.getBoundingBox().bottom else: center_y = 0 if x is not None: # We could find a place last_size = size last_priority = best_spot.priority arranger.place(x, y, hull_shape_arr) # take place before the next one grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) else: Logger.log("d", "Arrange all: could not find spot!") found_solution_for_all = False grouped_operation.addOperation(TranslateOperation(node, Vector(200, center_y, - idx * 20), set_position = True)) status_message.setProgress((idx + 1) / len(nodes_arr) * 100) Job.yieldThread() grouped_operation.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"), title = i18n_catalog.i18nc("@info:title", "Can't Find Location")) no_full_solution_message.show()
def run(self): status_message = Message( i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime=0, dismissable=False, progress=0, title=i18n_catalog.i18nc("@info:title", "Placing Object")) status_message.show() scene = Application.getInstance().getController().getScene() total_progress = len(self._objects) * self._count current_progress = 0 global_container_stack = Application.getInstance( ).getGlobalContainerStack() machine_width = global_container_stack.getProperty( "machine_width", "value") machine_depth = global_container_stack.getProperty( "machine_depth", "value") root = scene.getRoot() scale = 0.5 arranger = Arrange.create(x=machine_width, y=machine_depth, scene_root=root, scale=scale, min_offset=self._min_offset) processed_nodes = [] nodes = [] not_fit_count = 0 for node in self._objects: # If object is part of a group, multiply group current_node = node while current_node.getParent() and ( current_node.getParent().callDecoration("isGroup") or current_node.getParent().callDecoration("isSliceable")): current_node = current_node.getParent() if current_node in processed_nodes: continue processed_nodes.append(current_node) node_too_big = False if node.getBoundingBox( ).width < machine_width or node.getBoundingBox( ).depth < machine_depth: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode( current_node, min_offset=self._min_offset, scale=scale) else: node_too_big = True found_solution_for_all = True arranger.resetLastPriority() for i in range(self._count): # We do place the nodes one by one, as we want to yield in between. new_node = copy.deepcopy(node) solution_found = False if not node_too_big: solution_found = arranger.findNodePlacement( new_node, offset_shape_arr, hull_shape_arr) if node_too_big or not solution_found: found_solution_for_all = False new_location = new_node.getPosition() new_location = new_location.set(z=-not_fit_count * 20) new_node.setPosition(new_location) not_fit_count += 1 # Same build plate build_plate_number = current_node.callDecoration( "getBuildPlateNumber") new_node.callDecoration("setBuildPlateNumber", build_plate_number) for child in new_node.getChildren(): child.callDecoration("setBuildPlateNumber", build_plate_number) nodes.append(new_node) current_progress += 1 status_message.setProgress( (current_progress / total_progress) * 100) Job.yieldThread() Job.yieldThread() if nodes: op = GroupedOperation() for new_node in nodes: op.addOperation( AddSceneNodeOperation(new_node, current_node.getParent())) op.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc( "@info:status", "Unable to find a location within the build volume for all objects" ), title=i18n_catalog.i18nc( "@info:title", "Placing Object")) no_full_solution_message.show()
def run(self): status_message = Message( i18n_catalog.i18nc("@info:status", "Multiplying and placing objects"), lifetime=0, dismissable=False, progress=0, title=i18n_catalog.i18nc("@info:title", "Placing Object")) status_message.show() scene = Application.getInstance().getController().getScene() total_progress = len(self._objects) * self._count current_progress = 0 root = scene.getRoot() arranger = Arrange.create(scene_root=root) nodes = [] for node in self._objects: # If object is part of a group, multiply group current_node = node while current_node.getParent() and current_node.getParent( ).callDecoration("isGroup"): current_node = current_node.getParent() node_too_big = False if node.getBoundingBox().width < 300 or node.getBoundingBox( ).depth < 300: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode( current_node, min_offset=self._min_offset) else: node_too_big = True found_solution_for_all = True for i in range(self._count): # We do place the nodes one by one, as we want to yield in between. if not node_too_big: node, solution_found = arranger.findNodePlacement( current_node, offset_shape_arr, hull_shape_arr) if node_too_big or not solution_found: found_solution_for_all = False new_location = node.getPosition() new_location = new_location.set(z=100 - i * 20) node.setPosition(new_location) # Same build plate build_plate_number = current_node.callDecoration( "getBuildPlateNumber") node.callDecoration("setBuildPlateNumber", build_plate_number) nodes.append(node) current_progress += 1 status_message.setProgress( (current_progress / total_progress) * 100) Job.yieldThread() Job.yieldThread() if nodes: op = GroupedOperation() for new_node in nodes: print_mode_enabled = Application.getInstance( ).getGlobalContainerStack().getProperty( "print_mode", "enabled") if print_mode_enabled: node_dup = DuplicatedNode(new_node) op.addOperation( AddNodesOperation(node_dup, current_node.getParent())) else: op.addOperation( AddSceneNodeOperation(new_node, current_node.getParent())) op.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc( "@info:status", "Unable to find a location within the build volume for all objects" ), title=i18n_catalog.i18nc( "@info:title", "Placing Object")) no_full_solution_message.show()
def run(self): status_message = Message( i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime=0, dismissable=False, progress=0, title=i18n_catalog.i18nc("@info:title", "Finding Location")) status_message.show() # Collect nodes to be placed nodes_arr = [ ] # fill with (size, node, offset_shape_arr, hull_shape_arr) for node in self._nodes: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode( node, min_offset=self._min_offset) nodes_arr.append( (offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr)) # Sort the nodes with the biggest area first. nodes_arr.sort(key=lambda item: item[0]) nodes_arr.reverse() global_container_stack = Application.getInstance( ).getGlobalContainerStack() machine_width = global_container_stack.getProperty( "machine_width", "value") machine_depth = global_container_stack.getProperty( "machine_depth", "value") x, y = machine_width, machine_depth arrange_array = ArrangeArray(x=x, y=y, fixed_nodes=[]) arrange_array.add() # Place nodes one at a time start_priority = 0 grouped_operation = GroupedOperation() found_solution_for_all = True left_over_nodes = [] # nodes that do not fit on an empty build plate for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr): # For performance reasons, we assume that when a location does not fit, # it will also not fit for the next object (while what can be untrue). try_placement = True current_build_plate_number = 0 # always start with the first one while try_placement: # make sure that current_build_plate_number is not going crazy or you'll have a lot of arrange objects while current_build_plate_number >= arrange_array.count(): arrange_array.add() arranger = arrange_array.get(current_build_plate_number) best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): center_y = node.getWorldPosition().y - node.getBoundingBox( ).bottom else: center_y = 0 if x is not None: # We could find a place arranger.place( x, y, hull_shape_arr) # place the object in the arranger node.callDecoration("setBuildPlateNumber", current_build_plate_number) grouped_operation.addOperation( TranslateOperation(node, Vector(x, center_y, y), set_position=True)) try_placement = False else: # very naive, because we skip to the next build plate if one model doesn't fit. if arranger.isEmpty: # apparently we can never place this object left_over_nodes.append(node) try_placement = False else: # try next build plate current_build_plate_number += 1 try_placement = True status_message.setProgress((idx + 1) / len(nodes_arr) * 100) Job.yieldThread() for node in left_over_nodes: node.callDecoration("setBuildPlateNumber", -1) # these are not on any build plate found_solution_for_all = False grouped_operation.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc( "@info:status", "Unable to find a location within the build volume for all objects" ), title=i18n_catalog.i18nc( "@info:title", "Can't Find Location")) no_full_solution_message.show()
def run(self): status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime = 0, dismissable=False, progress = 0, title = i18n_catalog.i18nc("@info:title", "Finding Location")) status_message.show() # Collect nodes to be placed nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr) for node in self._nodes: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset) nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr)) # Sort the nodes with the biggest area first. nodes_arr.sort(key=lambda item: item[0]) nodes_arr.reverse() global_container_stack = Application.getInstance().getGlobalContainerStack() machine_width = global_container_stack.getProperty("machine_width", "value") machine_depth = global_container_stack.getProperty("machine_depth", "value") x, y = machine_width, machine_depth arrange_array = ArrangeArray(x = x, y = y, fixed_nodes = []) arrange_array.add() # Place nodes one at a time start_priority = 0 grouped_operation = GroupedOperation() found_solution_for_all = True left_over_nodes = [] # nodes that do not fit on an empty build plate for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr): # For performance reasons, we assume that when a location does not fit, # it will also not fit for the next object (while what can be untrue). try_placement = True current_build_plate_number = 0 # always start with the first one while try_placement: # make sure that current_build_plate_number is not going crazy or you'll have a lot of arrange objects while current_build_plate_number >= arrange_array.count(): arrange_array.add() arranger = arrange_array.get(current_build_plate_number) best_spot = arranger.bestSpot(hull_shape_arr, start_prio=start_priority) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): center_y = node.getWorldPosition().y - node.getBoundingBox().bottom else: center_y = 0 if x is not None: # We could find a place arranger.place(x, y, offset_shape_arr) # place the object in the arranger node.callDecoration("setBuildPlateNumber", current_build_plate_number) grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) try_placement = False else: # very naive, because we skip to the next build plate if one model doesn't fit. if arranger.isEmpty: # apparently we can never place this object left_over_nodes.append(node) try_placement = False else: # try next build plate current_build_plate_number += 1 try_placement = True status_message.setProgress((idx + 1) / len(nodes_arr) * 100) Job.yieldThread() for node in left_over_nodes: node.callDecoration("setBuildPlateNumber", -1) # these are not on any build plate found_solution_for_all = False grouped_operation.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"), title = i18n_catalog.i18nc("@info:title", "Can't Find Location")) no_full_solution_message.show()
def _readMeshFinished(self, job): Logger.log("d", "read mesh finisihed!") ### START PATCH: detect belt printer global_container_stack = self._application.getGlobalContainerStack() if not global_container_stack: return is_belt_printer = self._preferences.getValue("BeltPlugin/on_plugin") ### END PATCH nodes = job.getResult() file_name = job.getFileName() file_name_lower = file_name.lower() file_extension = file_name_lower.split(".")[-1] self._application._currently_loading_files.remove(file_name) self._application.fileLoaded.emit(file_name) target_build_plate = self._application.getMultiBuildPlateModel( ).activeBuildPlate root = self._application.getController().getScene().getRoot() fixed_nodes = [] for node_ in DepthFirstIterator(root): if node_.callDecoration("isSliceable") and node_.callDecoration( "getBuildPlateNumber") == target_build_plate: fixed_nodes.append(node_) global_container_stack = self._application.getGlobalContainerStack() machine_width = global_container_stack.getProperty( "machine_width", "value") machine_depth = global_container_stack.getProperty( "machine_depth", "value") arranger = Arrange.create(x=machine_width, y=machine_depth, fixed_nodes=fixed_nodes) min_offset = 8 default_extruder_position = self._application.getMachineManager( ).defaultExtruderPosition default_extruder_id = self._application._global_container_stack.extruders[ default_extruder_position].getId() select_models_on_load = self._application.getPreferences().getValue( "cura/select_models_on_load") for original_node in nodes: # Create a CuraSceneNode just if the original node is not that type if isinstance(original_node, CuraSceneNode): node = original_node else: node = CuraSceneNode() node.setMeshData(original_node.getMeshData()) #Setting meshdata does not apply scaling. if (original_node.getScale() != Vector(1.0, 1.0, 1.0)): node.scale(original_node.getScale()) node.setSelectable(True) node.setName(os.path.basename(file_name)) self._application.getBuildVolume().checkBoundsAndUpdate(node) is_non_sliceable = "." + file_extension in self._application._non_sliceable_extensions if is_non_sliceable: self._application.callLater( lambda: self._application.getController().setActiveView( "SimulationView")) block_slicing_decorator = BlockSlicingDecorator() node.addDecorator(block_slicing_decorator) else: sliceable_decorator = SliceableObjectDecorator() node.addDecorator(sliceable_decorator) scene = self._application.getController().getScene() # If there is no convex hull for the node, start calculating it and continue. if not node.getDecorator(ConvexHullDecorator): node.addDecorator(ConvexHullDecorator()) for child in node.getAllChildren(): if not child.getDecorator(ConvexHullDecorator): child.addDecorator(ConvexHullDecorator()) ### START PATCH: don't do standard arrange on load for belt printers ### but place in a line instead if is_belt_printer: half_node_depth = node.getBoundingBox().depth / 2 build_plate_empty = True leading_edge = self._application.getBuildVolume( ).getBoundingBox().front for existing_node in DepthFirstIterator(root): if (not issubclass(type(existing_node), CuraSceneNode) or (not existing_node.getMeshData() and not existing_node.callDecoration("getLayerData")) or (existing_node.callDecoration("getBuildPlateNumber") != target_build_plate)): continue build_plate_empty = False leading_edge = min(leading_edge, existing_node.getBoundingBox().back) if not build_plate_empty or leading_edge < half_node_depth: node.setPosition( Vector( 0, 0, leading_edge - half_node_depth - self._margin_between_models)) if file_extension != "3mf" and not is_belt_printer: ### END PATCH if node.callDecoration("isSliceable"): # Only check position if it's not already blatantly obvious that it won't fit. if node.getBoundingBox( ) is None or self._application._volume.getBoundingBox( ) is None or node.getBoundingBox( ).width < self._application._volume.getBoundingBox( ).width or node.getBoundingBox( ).depth < self._application._volume.getBoundingBox().depth: # Find node location offset_shape_arr, hull_shape_arr = ShapeArray.fromNode( node, min_offset=min_offset) # If a model is to small then it will not contain any points if offset_shape_arr is None and hull_shape_arr is None: Message(self._application._i18n_catalog.i18nc( "@info:status", "The selected model was too small to load."), title=self._application._i18n_catalog. i18nc("@info:title", "Warning")).show() return # Step is for skipping tests to make it a lot faster. it also makes the outcome somewhat rougher arranger.findNodePlacement(node, offset_shape_arr, hull_shape_arr, step=10) # This node is deep copied from some other node which already has a BuildPlateDecorator, but the deepcopy # of BuildPlateDecorator produces one that's associated with build plate -1. So, here we need to check if # the BuildPlateDecorator exists or not and always set the correct build plate number. build_plate_decorator = node.getDecorator(BuildPlateDecorator) if build_plate_decorator is None: build_plate_decorator = BuildPlateDecorator(target_build_plate) node.addDecorator(build_plate_decorator) build_plate_decorator.setBuildPlateNumber(target_build_plate) op = AddSceneNodeOperation(node, scene.getRoot()) op.push() node.callDecoration("setActiveExtruder", default_extruder_id) scene.sceneChanged.emit(node) if select_models_on_load: Selection.add(node) self._application.fileCompleted.emit(file_name)
def run(self): status_message = Message(i18n_catalog.i18nc("@info:status", "Finding new location for objects"), lifetime = 0, dismissable=False, progress = 0, title = i18n_catalog.i18nc("@info:title", "Finding Location")) status_message.show() arranger = Arrange.create(fixed_nodes = self._fixed_nodes) # Collect nodes to be placed nodes_arr = [] # fill with (size, node, offset_shape_arr, hull_shape_arr) for node in self._nodes: offset_shape_arr, hull_shape_arr = ShapeArray.fromNode(node, min_offset = self._min_offset) nodes_arr.append((offset_shape_arr.arr.shape[0] * offset_shape_arr.arr.shape[1], node, offset_shape_arr, hull_shape_arr)) # Sort the nodes with the biggest area first. nodes_arr.sort(key=lambda item: item[0]) nodes_arr.reverse() # Place nodes one at a time start_priority = 0 last_priority = start_priority last_size = None grouped_operation = GroupedOperation() found_solution_for_all = True for idx, (size, node, offset_shape_arr, hull_shape_arr) in enumerate(nodes_arr): # For performance reasons, we assume that when a location does not fit, # it will also not fit for the next object (while what can be untrue). # We also skip possibilities by slicing through the possibilities (step = 10) if last_size == size: # This optimization works if many of the objects have the same size start_priority = last_priority else: start_priority = 0 best_spot = arranger.bestSpot(offset_shape_arr, start_prio=start_priority, step=10) x, y = best_spot.x, best_spot.y node.removeDecorator(ZOffsetDecorator) if node.getBoundingBox(): center_y = node.getWorldPosition().y - node.getBoundingBox().bottom else: center_y = 0 if x is not None: # We could find a place last_size = size last_priority = best_spot.priority arranger.place(x, y, hull_shape_arr) # take place before the next one grouped_operation.addOperation(TranslateOperation(node, Vector(x, center_y, y), set_position = True)) else: Logger.log("d", "Arrange all: could not find spot!") found_solution_for_all = False grouped_operation.addOperation(TranslateOperation(node, Vector(200, center_y, - idx * 20), set_position = True)) status_message.setProgress((idx + 1) / len(nodes_arr) * 100) Job.yieldThread() grouped_operation.push() status_message.hide() if not found_solution_for_all: no_full_solution_message = Message(i18n_catalog.i18nc("@info:status", "Unable to find a location within the build volume for all objects"), title = i18n_catalog.i18nc("@info:title", "Can't Find Location")) no_full_solution_message.show() self.finished.emit(self)
def _onPrintModeChanged(self): if self._global_stack: self._restoreSettingsValue() print_mode = self._global_stack.getProperty("print_mode", "value") if print_mode != "regular": nodes = self._scene.getRoot().getChildren() max_offset = 0 machine_head_with_fans_polygon = self._global_stack.getProperty( "machine_head_with_fans_polygon", "value") machine_head_size = abs(machine_head_with_fans_polygon[0][0] - machine_head_with_fans_polygon[2][0]) adhesion_type = self._global_stack.getProperty( "adhesion_type", "value") if adhesion_type == "skirt": margin = self._global_stack.getProperty( "skirt_gap", "value") elif adhesion_type == "brim": margin = self._global_stack.getProperty( "brim_width", "value") elif adhesion_type == "raft": margin = self._global_stack.getProperty( "raft_margin", "value") else: margin = 0 if print_mode == "mirror": margin += machine_head_size / 2 sliceable_nodes = [] for node in nodes: self._setActiveExtruder(node) if (node.callDecoration("isSliceable") or node.callDecoration("isGroup")) and not isinstance( node, DuplicatedNode): sliceable_nodes.append(node) offset_shape_arr, hull_shape_arr = ShapeArray.fromNode( node, 4) position = node.getPosition() max_offset = max( abs(offset_shape_arr.offset_x) + position.x + margin, max_offset) for node in sliceable_nodes: position = node.getPosition() offset = position.x - max_offset node.setPosition(Vector(offset, position.y, position.z)) if self._old_material == "": self._old_material = ExtruderManager.getInstance( ).getExtruderStack(1).material material = ExtruderManager.getInstance().getExtruderStack( 0).material ExtruderManager.getInstance().getExtruderStack( 1).setMaterial(material) variant = ExtruderManager.getInstance().getExtruderStack( 0).variant ExtruderManager.getInstance().getExtruderStack( 1).setVariant(variant) Preferences.getInstance().setValue( "cura/old_material", self._old_material.getId()) self.renderDuplicatedNodes() else: self.removeDuplicatedNodes() if self._old_material != "": ExtruderManager.getInstance().getExtruderStack( 1).setMaterial(self._old_material) self._old_material = "" Preferences.getInstance().setValue("cura/old_material", "")