diff --git a/cura/Arranging/ArrangeObjectsJob.py b/cura/Arranging/ArrangeObjectsJob.py index 48d2436482..d494a79327 100644 --- a/cura/Arranging/ArrangeObjectsJob.py +++ b/cura/Arranging/ArrangeObjectsJob.py @@ -40,7 +40,7 @@ class ArrangeObjectsJob(Job): found_solution_for_all = False try: - found_solution_for_all = arranger.arrange() + found_solution_for_all = arranger.arrange(only_if_full_success = True) 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 index fd93ab1846..d0a78431f6 100644 --- a/cura/Arranging/Arranger.py +++ b/cura/Arranging/Arranger.py @@ -16,12 +16,16 @@ 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, only_if_full_success: 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.push() - return not_fit_count == 0 + full_success = not_fit_count == 0 + + if full_success or not only_if_full_success: + grouped_operation.push() + + return full_success diff --git a/cura/Arranging/Nest2DArrange.py b/cura/Arranging/Nest2DArrange.py index 968522d5a3..5f34cb21a0 100644 --- a/cura/Arranging/Nest2DArrange.py +++ b/cura/Arranging/Nest2DArrange.py @@ -54,22 +54,6 @@ class Nest2DArrange(Arranger): machine_depth = self._build_volume.getDepth() - (edge_disallowed_size * 2) build_plate_bounding_box = Box(int(machine_width * self._factor), int(machine_depth * self._factor)) - if self._fixed_nodes is None: - self._fixed_nodes = [] - - # 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] * self._factor), int(point[1] * self._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 @@ -80,40 +64,66 @@ class Nest2DArrange(Arranger): [half_machine_width, half_machine_depth] ], numpy.float32)) - disallowed_areas = self._build_volume.getDisallowedAreas() - for area in disallowed_areas: - converted_points = [] + def _convert_points(points): + if points is not None and len(points) > 2: # numpy array has to be explicitly checked against None + converted_points = [] + for point in points: + converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor))) + return [converted_points] + else: + return [] + polygons_nodes_to_arrange = [] + 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 + + polygons_nodes_to_arrange += _convert_points(hull_polygon.getPoints()) + + polygons_disallowed_areas = [] + for area in self._build_volume.getDisallowedAreas(): # 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] * self._factor), int(point[1] * self._factor))) + polygons_disallowed_areas += _convert_points(clipped_area.getPoints()) - disallowed_area = Item(converted_points) + polygons_fixed_nodes = [] + if self._fixed_nodes is None: + self._fixed_nodes = [] + for node in self._fixed_nodes: + hull_polygon = node.callDecoration("getConvexHull") + + if hull_polygon is not None: + polygons_fixed_nodes += _convert_points(hull_polygon.getPoints()) + + strategies = [NfpConfig.Alignment.CENTER, + NfpConfig.Alignment.BOTTOM_LEFT, + NfpConfig.Alignment.BOTTOM_RIGHT, + NfpConfig.Alignment.TOP_LEFT, + NfpConfig.Alignment.TOP_RIGHT] + found_solution_for_all = False + while not found_solution_for_all and len(strategies) > 0: + + # Add all the items we want to arrange + node_items = [] + for polygon in polygons_nodes_to_arrange: + node_items.append(Item(polygon)) + + for polygon in polygons_disallowed_areas: + disallowed_area = Item(polygon) disallowed_area.markAsDisallowedAreaInBin(0) node_items.append(disallowed_area) - for node in self._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 - for point in hull_polygon.getPoints(): - converted_points.append(Point(int(point[0] * self._factor), int(point[1] * self._factor))) - item = Item(converted_points) + for polygon in polygons_fixed_nodes: + item = Item(polygon) item.markAsFixedInBin(0) node_items.append(item) - strategies = [NfpConfig.Alignment.CENTER] * 3 + [NfpConfig.Alignment.BOTTOM_LEFT] * 3 - found_solution_for_all = False - while not found_solution_for_all and len(strategies) > 0: config = NfpConfig() config.accuracy = 1.0 - config.alignment = NfpConfig.Alignment.CENTER + config.alignment = NfpConfig.Alignment.DONT_ALIGN config.starting_point = strategies[0] strategies = strategies[1:] diff --git a/cura/MultiplyObjectsJob.py b/cura/MultiplyObjectsJob.py index 889b6f5d1a..024baa50f5 100644 --- a/cura/MultiplyObjectsJob.py +++ b/cura/MultiplyObjectsJob.py @@ -84,6 +84,7 @@ class MultiplyObjectsJob(Job): arranger = Nest2DArrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes, factor=1000) group_operation, not_fit_count = arranger.createGroupOperationForArrange(add_new_nodes_in_scene=True) + found_solution_for_all = not_fit_count == 0 if nodes_to_add_without_arrange: for nested_node in nodes_to_add_without_arrange: diff --git a/resources/definitions/ultimaker_s8.def.json b/resources/definitions/ultimaker_s8.def.json index cc9c97aebd..fd758f6e2a 100644 --- a/resources/definitions/ultimaker_s8.def.json +++ b/resources/definitions/ultimaker_s8.def.json @@ -92,10 +92,10 @@ "cool_min_temperature": { "value": "material_print_temperature-15" }, "default_material_print_temperature": { "maximum_value_warning": 320 }, "extra_infill_lines_to_support_skins": { "value": "'walls_and_lines'" }, - "flooring_layer_count": { "value": "1" }, + "flooring_layer_count": { "value": 0 }, "gradual_flow_enabled": { "value": false }, "hole_xy_offset": { "value": 0.075 }, - "infill_material_flow": { "value": "1.1*material_flow" }, + "infill_material_flow": { "value": "material_flow" }, "infill_overlap": { "value": 10 }, "infill_pattern": { "value": "'zigzag' if infill_sparse_density > 80 else 'grid'" }, "infill_sparse_density": { "value": 15 }, @@ -374,7 +374,7 @@ "speed_travel": { "maximum_value": 500, - "value": 500 + "value": 400 }, "speed_travel_layer_0": { @@ -427,7 +427,6 @@ "support_tree_bp_diameter": { "value": 15 }, "support_tree_tip_diameter": { "value": 1.0 }, "support_tree_top_rate": { "value": 20 }, - "support_wall_count": { "value": 2 }, "support_xy_distance_overhang": { "value": "machine_nozzle_size" }, "support_z_distance": { "value": "0.4*material_shrinkage_percentage_z/100.0" }, "top_bottom_thickness": { "value": "round(4*layer_height, 2)" }, diff --git a/resources/definitions/ultimaker_sketch_sprint.def.json b/resources/definitions/ultimaker_sketch_sprint.def.json index d0991231bc..529abc0940 100644 --- a/resources/definitions/ultimaker_sketch_sprint.def.json +++ b/resources/definitions/ultimaker_sketch_sprint.def.json @@ -397,8 +397,6 @@ "z_seam_corner": { "value": "'z_seam_corner_inner'" }, "z_seam_position": { "value": "'backleft'" }, "z_seam_type": { "value": "'sharpest_corner'" }, - "z_seam_x": { "value": 150 }, - "z_seam_y": { "value": 180 }, "zig_zaggify_infill": { "value": true } } } \ No newline at end of file