Merge branch '5.10' into CURA-12428_Changelog510

This commit is contained in:
Erwan MATHIEU 2025-03-10 16:00:56 +01:00 committed by GitHub
commit bc8ee22681
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 59 additions and 47 deletions

View File

@ -40,7 +40,7 @@ class ArrangeObjectsJob(Job):
found_solution_for_all = False found_solution_for_all = False
try: 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 except: # If the thread crashes, the message should still close
Logger.logException("e", Logger.logException("e",
"Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.") "Unable to arrange the objects on the buildplate. The arrange algorithm has crashed.")

View File

@ -16,12 +16,16 @@ class Arranger:
""" """
raise NotImplementedError 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. 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 :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 :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)
grouped_operation.push() full_success = not_fit_count == 0
return not_fit_count == 0
if full_success or not only_if_full_success:
grouped_operation.push()
return full_success

View File

@ -54,22 +54,6 @@ class Nest2DArrange(Arranger):
machine_depth = self._build_volume.getDepth() - (edge_disallowed_size * 2) 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)) 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) # 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_width = 0.5 * machine_width - 1
half_machine_depth = 0.5 * machine_depth - 1 half_machine_depth = 0.5 * machine_depth - 1
@ -80,40 +64,66 @@ class Nest2DArrange(Arranger):
[half_machine_width, half_machine_depth] [half_machine_width, half_machine_depth]
], numpy.float32)) ], numpy.float32))
disallowed_areas = self._build_volume.getDisallowedAreas() def _convert_points(points):
for area in disallowed_areas: if points is not None and len(points) > 2: # numpy array has to be explicitly checked against None
converted_points = [] 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) # Clip the disallowed areas so that they don't overlap the bounding box (The arranger chokes otherwise)
clipped_area = area.intersectionConvexHulls(build_plate_polygon) clipped_area = area.intersectionConvexHulls(build_plate_polygon)
if clipped_area.getPoints() is not None and len( polygons_disallowed_areas += _convert_points(clipped_area.getPoints())
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)))
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) disallowed_area.markAsDisallowedAreaInBin(0)
node_items.append(disallowed_area) node_items.append(disallowed_area)
for node in self._fixed_nodes: for polygon in polygons_fixed_nodes:
converted_points = [] item = Item(polygon)
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)
item.markAsFixedInBin(0) item.markAsFixedInBin(0)
node_items.append(item) 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 = NfpConfig()
config.accuracy = 1.0 config.accuracy = 1.0
config.alignment = NfpConfig.Alignment.CENTER config.alignment = NfpConfig.Alignment.DONT_ALIGN
config.starting_point = strategies[0] config.starting_point = strategies[0]
strategies = strategies[1:] strategies = strategies[1:]

View File

@ -84,6 +84,7 @@ class MultiplyObjectsJob(Job):
arranger = Nest2DArrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes, factor=1000) arranger = Nest2DArrange(nodes, Application.getInstance().getBuildVolume(), fixed_nodes, factor=1000)
group_operation, not_fit_count = arranger.createGroupOperationForArrange(add_new_nodes_in_scene=True) 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: 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

@ -92,10 +92,10 @@
"cool_min_temperature": { "value": "material_print_temperature-15" }, "cool_min_temperature": { "value": "material_print_temperature-15" },
"default_material_print_temperature": { "maximum_value_warning": 320 }, "default_material_print_temperature": { "maximum_value_warning": 320 },
"extra_infill_lines_to_support_skins": { "value": "'walls_and_lines'" }, "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 }, "gradual_flow_enabled": { "value": false },
"hole_xy_offset": { "value": 0.075 }, "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_overlap": { "value": 10 },
"infill_pattern": { "value": "'zigzag' if infill_sparse_density > 80 else 'grid'" }, "infill_pattern": { "value": "'zigzag' if infill_sparse_density > 80 else 'grid'" },
"infill_sparse_density": { "value": 15 }, "infill_sparse_density": { "value": 15 },
@ -374,7 +374,7 @@
"speed_travel": "speed_travel":
{ {
"maximum_value": 500, "maximum_value": 500,
"value": 500 "value": 400
}, },
"speed_travel_layer_0": "speed_travel_layer_0":
{ {
@ -427,7 +427,6 @@
"support_tree_bp_diameter": { "value": 15 }, "support_tree_bp_diameter": { "value": 15 },
"support_tree_tip_diameter": { "value": 1.0 }, "support_tree_tip_diameter": { "value": 1.0 },
"support_tree_top_rate": { "value": 20 }, "support_tree_top_rate": { "value": 20 },
"support_wall_count": { "value": 2 },
"support_xy_distance_overhang": { "value": "machine_nozzle_size" }, "support_xy_distance_overhang": { "value": "machine_nozzle_size" },
"support_z_distance": { "value": "0.4*material_shrinkage_percentage_z/100.0" }, "support_z_distance": { "value": "0.4*material_shrinkage_percentage_z/100.0" },
"top_bottom_thickness": { "value": "round(4*layer_height, 2)" }, "top_bottom_thickness": { "value": "round(4*layer_height, 2)" },

View File

@ -397,8 +397,6 @@
"z_seam_corner": { "value": "'z_seam_corner_inner'" }, "z_seam_corner": { "value": "'z_seam_corner_inner'" },
"z_seam_position": { "value": "'backleft'" }, "z_seam_position": { "value": "'backleft'" },
"z_seam_type": { "value": "'sharpest_corner'" }, "z_seam_type": { "value": "'sharpest_corner'" },
"z_seam_x": { "value": 150 },
"z_seam_y": { "value": 180 },
"zig_zaggify_infill": { "value": true } "zig_zaggify_infill": { "value": true }
} }
} }