From d5a099f84bc500b3e37676de60d201ffe1e44d14 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Wed, 20 Sep 2023 13:47:09 +0200 Subject: [PATCH 01/30] Start 5.5.x release branch. --- conanfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/conanfile.py b/conanfile.py index 7d4d5dc601..381b6e2366 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-alpha" + self.version = "5.5.0-beta.1" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder) @@ -82,10 +82,10 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/5.3.0") + self.requires("arcus/(latest)@ultimaker/stable") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/stable") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 3a849553c4952e6f8f959729fe90d285a7ab8d23 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Wed, 20 Sep 2023 14:58:40 +0200 Subject: [PATCH 02/30] use correct versions --- conanfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conanfile.py b/conanfile.py index 381b6e2366..dea1860300 100644 --- a/conanfile.py +++ b/conanfile.py @@ -82,10 +82,10 @@ def build_requirements(self): def requirements(self): if self.options.enable_arcus: - self.requires("arcus/(latest)@ultimaker/stable") + self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/stable") + self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 43955a199c60077b74012d9a47297c694e5c2d91 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 25 Sep 2023 13:53:00 +0200 Subject: [PATCH 03/30] Support brim printed only at layer 0 CURA-10968 --- src/FffGcodeWriter.cpp | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..0721bd7628 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,24 +1232,28 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer + //support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + if (layer_nr == 0) { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - gcode_layer.addLinesByOptimizer( - support_brim_lines, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements = {}); + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) + { + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + gcode_layer.addLinesByOptimizer( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); + } } } From 26c7522af035fc5e629f72b398be78f8541f0a1f Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Mon, 25 Sep 2023 11:53:42 +0000 Subject: [PATCH 04/30] Applied clang-format. --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 0721bd7628..ef5d748533 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,7 +1232,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer - //support brim is only added in layer 0 + // support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. if (layer_nr == 0) { From e2203b07fd02668c1c7010c596ef84f6ff766b69 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Mon, 25 Sep 2023 13:55:28 +0200 Subject: [PATCH 05/30] Comment fix CURA-10968 --- src/FffGcodeWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index ef5d748533..68591de295 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1232,7 +1232,7 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer - // support brim is only added in layer 0 + // Support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. if (layer_nr == 0) { From 321d564cda3865c4be12f8c29ccaeac9219a535f Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 25 Sep 2023 14:53:50 +0200 Subject: [PATCH 06/30] Group outer wall order For the gradual flow we do want to group the outer walls in order to limit the number of flow changes. CURA-11082 --- src/InsetOrderOptimizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 006296fc68..86305d27b5 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -93,7 +93,7 @@ bool InsetOrderOptimizer::addToLayer() constexpr bool detect_loops = false; constexpr Polygons* combing_boundary = nullptr; - constexpr bool group_outer_walls = false; + const auto group_outer_walls = false; // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. PathOrderOptimizer From ec8bcfc3ad346fa21dde94b188caa84e18fcae06 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Mon, 25 Sep 2023 15:05:24 +0200 Subject: [PATCH 07/30] Add setting to group outer walls CURA-11082 --- src/InsetOrderOptimizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/InsetOrderOptimizer.cpp b/src/InsetOrderOptimizer.cpp index 86305d27b5..05b1fa3f3f 100644 --- a/src/InsetOrderOptimizer.cpp +++ b/src/InsetOrderOptimizer.cpp @@ -93,7 +93,7 @@ bool InsetOrderOptimizer::addToLayer() constexpr bool detect_loops = false; constexpr Polygons* combing_boundary = nullptr; - const auto group_outer_walls = false; + const auto group_outer_walls = settings.get("group_outer_walls"); // When we alternate walls, also alternate the direction at which the first wall starts in. // On even layers we start with normal direction, on odd layers with inverted direction. PathOrderOptimizer From d78bb6de2d5e85d054b7b46051661a98e75cd602 Mon Sep 17 00:00:00 2001 From: Remco Burema Date: Tue, 26 Sep 2023 19:59:27 +0200 Subject: [PATCH 08/30] Add missing 'perform_prime' member-variable. Note that this needs the updated GRPC definitions as well! -- This caused that variable to always be (re)set to false, even when no plugin was present, due to the 'identity' conversion taking place. (Maybe we should stop that in general, since that implies that we copy the data even when no plugin is present.) In any case, even if it wouldn't have caused this particular bug, it's an oversight that should be fixed anyway. should fix CURA-11084 --- src/plugins/converters.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/converters.cpp b/src/plugins/converters.cpp index 6e93f34afd..72a8166de7 100644 --- a/src/plugins/converters.cpp +++ b/src/plugins/converters.cpp @@ -345,6 +345,7 @@ gcode_paths_modify_request::value_type gcode_path->set_retract(path.retract); gcode_path->set_unretract_before_last_travel_move(path.unretract_before_last_travel_move); gcode_path->set_perform_z_hop(path.perform_z_hop); + gcode_path->set_perform_prime(path.perform_prime); gcode_path->set_skip_agressive_merge_hint(path.skip_agressive_merge_hint); gcode_path->set_done(path.done); gcode_path->set_fan_speed(path.getFanSpeed()); @@ -458,6 +459,7 @@ gcode_paths_modify_response::native_value_type .retract = gcode_path_msg.retract(), .unretract_before_last_travel_move = gcode_path_msg.unretract_before_last_travel_move(), .perform_z_hop = gcode_path_msg.perform_z_hop(), + .perform_prime = gcode_path_msg.perform_prime(), .skip_agressive_merge_hint = gcode_path_msg.skip_agressive_merge_hint(), .done = gcode_path_msg.done(), .fan_speed = gcode_path_msg.fan_speed(), From f832ec814da6d7d0e817460f65b4bfa74d13f5bb Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 29 Sep 2023 11:32:03 +0200 Subject: [PATCH 09/30] Comment fix CURA-10968 --- src/FffGcodeWriter.cpp | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 68591de295..9a404fa292 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1234,26 +1234,23 @@ void FffGcodeWriter::processSkirtBrim(const SliceDataStorage& storage, LayerPlan // Add the support brim after the skirt_brim to gcode_layer // Support brim is only added in layer 0 // For support brim we don't care about the order, because support doesn't need to be accurate. - if (layer_nr == 0) + const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; + if ((layer_nr == 0) && (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr)) { - const Settings& mesh_group_settings = Application::getInstance().current_slice->scene.current_mesh_group->settings; - if (extruder_nr == mesh_group_settings.get("support_extruder_nr_layer_0").extruder_nr) - { - total_line_count += storage.support_brim.size(); - Polygons support_brim_lines = storage.support_brim; - support_brim_lines.toPolylines(); - gcode_layer.addLinesByOptimizer( - support_brim_lines, - gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], - SpaceFillType::PolyLines, - enable_travel_optimization, - wipe_dist, - flow_ratio, - start_close_to, - fan_speed, - reverse_print_direction, - order_requirements = {}); - } + total_line_count += storage.support_brim.size(); + Polygons support_brim_lines = storage.support_brim; + support_brim_lines.toPolylines(); + gcode_layer.addLinesByOptimizer( + support_brim_lines, + gcode_layer.configs_storage.skirt_brim_config_per_extruder[extruder_nr], + SpaceFillType::PolyLines, + enable_travel_optimization, + wipe_dist, + flow_ratio, + start_close_to, + fan_speed, + reverse_print_direction, + order_requirements = {}); } } From f063b5ab7df6747b7c3092beec8ea90d042aa05b Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Tue, 3 Oct 2023 12:24:18 +0200 Subject: [PATCH 10/30] Reverted the changes done in CURA-9521 CURA-11109 --- include/FffGcodeWriter.h | 3 +-- include/infill.h | 7 ++----- src/FffGcodeWriter.cpp | 9 +++------ src/infill.cpp | 10 ++++------ 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/include/FffGcodeWriter.h b/include/FffGcodeWriter.h index 0e3dea645c..96f504c1e3 100644 --- a/include/FffGcodeWriter.h +++ b/include/FffGcodeWriter.h @@ -569,8 +569,7 @@ class FffGcodeWriter : public NoCopy const Ratio skin_density, const bool monotonic, bool& added_something, - double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT, - const bool is_bridge_skin = false) const; + double fan_speed = GCodePathConfig::FAN_SPEED_DEFAULT) const; /*! * see if we can avoid printing a lines or zig zag style skin part in multiple segments by moving to diff --git a/include/infill.h b/include/infill.h index 40ca7fbd59..289a0850bb 100644 --- a/include/infill.h +++ b/include/infill.h @@ -206,8 +206,7 @@ class Infill const std::shared_ptr& cross_fill_provider = nullptr, const std::shared_ptr& lightning_layer = nullptr, const SliceMeshStorage* mesh = nullptr, - const Polygons& prevent_small_exposed_to_air = Polygons(), - const bool is_bridge_skin = false); + const Polygons& prevent_small_exposed_to_air = Polygons()); /*! * Generate the wall toolpaths of an infill area. It will return the inner contour and set the inner-contour. @@ -219,7 +218,6 @@ class Infill * \param line_width [in] The optimum wall line width of the walls * \param infill_overlap [in] The overlap of the infill * \param settings [in] A settings storage to use for generating variable-width walls. - * \param is_bridge_skin [in] Setting to filter out the extra skin walls while bridging * \return The inner contour of the wall toolpaths */ static Polygons generateWallToolPaths( @@ -230,8 +228,7 @@ class Infill const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type, - const bool is_bridge_skin = false); + SectionType section_type); private: /*! diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..2af62a6824 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2727,8 +2727,7 @@ void FffGcodeWriter::processTopBottom( skin_density, monotonic, added_something, - fan_speed, - is_bridge_skin); + fan_speed); } void FffGcodeWriter::processSkinPrintFeature( @@ -2745,8 +2744,7 @@ void FffGcodeWriter::processSkinPrintFeature( const Ratio skin_density, const bool monotonic, bool& added_something, - double fan_speed, - const bool is_bridge_skin) const + double fan_speed) const { Polygons skin_polygons; Polygons skin_lines; @@ -2806,8 +2804,7 @@ void FffGcodeWriter::processSkinPrintFeature( nullptr, nullptr, nullptr, - small_areas_on_surface ? Polygons() : exposed_to_air, - is_bridge_skin); + small_areas_on_surface ? Polygons() : exposed_to_air); // add paths if (! skin_polygons.empty() || ! skin_lines.empty() || ! skin_paths.empty()) diff --git a/src/infill.cpp b/src/infill.cpp index 9137aa66ca..6da3c25c4f 100644 --- a/src/infill.cpp +++ b/src/infill.cpp @@ -60,14 +60,13 @@ Polygons Infill::generateWallToolPaths( const coord_t infill_overlap, const Settings& settings, int layer_idx, - SectionType section_type, - const bool is_bridge_skin) + SectionType section_type) { outer_contour = outer_contour.offset(infill_overlap); scripta::log("infill_outer_contour", outer_contour, section_type, layer_idx, scripta::CellVDI{ "infill_overlap", infill_overlap }); Polygons inner_contour; - if ((wall_line_count > 0) && (! is_bridge_skin)) + if (wall_line_count > 0) { constexpr coord_t wall_0_inset = 0; // Don't apply any outer wall inset for these. That's just for the outer wall. WallToolPaths wall_toolpaths(outer_contour, line_width, wall_line_count, wall_0_inset, settings, layer_idx, section_type); @@ -91,15 +90,14 @@ void Infill::generate( const std::shared_ptr& cross_fill_provider, const std::shared_ptr& lightning_trees, const SliceMeshStorage* mesh, - const Polygons& prevent_small_exposed_to_air, - const bool is_bridge_skin) + const Polygons& prevent_small_exposed_to_air) { if (outer_contour.empty()) { return; } - inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type, is_bridge_skin); + inner_contour = generateWallToolPaths(toolpaths, outer_contour, wall_line_count, infill_line_width, infill_overlap, settings, layer_idx, section_type); scripta::log("infill_inner_contour_0", inner_contour, section_type, layer_idx); // It does not make sense to print a pattern in a small region. So the infill region From 873800b9a36cb736a5c21acfbbc1e436c9bbab87 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 3 Oct 2023 19:39:58 +0200 Subject: [PATCH 11/30] Use correct path config CURA-11119 CURA-11121 --- src/FffGcodeWriter.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 235f6f587e..fd80cd7d04 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2833,10 +2833,10 @@ void FffGcodeWriter::processSkinPrintFeature( gcode_layer, mesh.settings, extruder_nr, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, - mesh_config.skin_config, + config, + config, + config, + config, retract_before_outer_wall, wipe_dist, wipe_dist, From a70793c1a98825020a66d3d72176ad5412cf5868 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 5 Oct 2023 09:54:12 +0200 Subject: [PATCH 12/30] Change seam position scoring to make it more consistent CURA-11100 --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 93198fcd19..464810cefa 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -646,7 +646,7 @@ class PathOrderOptimizer // so the user has some control over where the seam will lie. // the divisor here may need adjusting to obtain the best results (TBD) - corner_shift = score_distance / 10; + corner_shift = score_distance / 50; } float score = score_distance; From 3bce9f8c2eb62b581cd18aa9c9a273616a3af439 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 5 Oct 2023 11:48:49 +0200 Subject: [PATCH 13/30] Remove unused variable boyscouting CURA-11110 --- src/skin.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/skin.cpp b/src/skin.cpp index 227591a8c4..28fc60c238 100644 --- a/src/skin.cpp +++ b/src/skin.cpp @@ -346,8 +346,6 @@ void SkinInfillAreaComputation::generateRoofingFillAndSkinFill(SliceLayerPart& p */ Polygons SkinInfillAreaComputation::generateFilledAreaAbove(SliceLayerPart& part, size_t roofing_layer_count) { - const size_t wall_idx = std::min(size_t(2), mesh.settings.get("wall_line_count")); - Polygons filled_area_above = getOutlineOnLayer(part, layer_nr + roofing_layer_count); if (! no_small_gaps_heuristic) { From 3787b2c73369052182ea2b359f372f38f5dc5fe5 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Thu, 5 Oct 2023 12:45:17 +0200 Subject: [PATCH 14/30] Use different config for roofing inner/outer walls Implementation is not fully desired since it has some downsides 1. When a layer-part is partially a roof the whole outer/inner wall uses the inner/outer wall roofing print-configuration 2. Some logic is duplicated, namely the function that calculates the `filled_area_above`. This function previously lived in `SkinInfillAreaComputation`. It's not logical to create a `SkinInfillAreaComputation` instance just to re-use this utility. Proposal to fix this is to move the logic of calculating the `filled_area_above` to a more central location, this will be done in a seperate ticket. 3. The `inset0_roofing_config` and `insetX_roofing_config` contain `line_width` properties. Changing the line-widths here doesn't actually change the line width. The line widths can only be changed through the `insetX_config` and `inset0_config` configs CURA-11110 --- include/settings/MeshPathConfigs.h | 2 + src/FffGcodeWriter.cpp | 96 +++++++++++++++++++++++++++++- src/settings/MeshPathConfigs.cpp | 26 ++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) diff --git a/include/settings/MeshPathConfigs.h b/include/settings/MeshPathConfigs.h index 43457fbc8c..61c993b9a8 100644 --- a/include/settings/MeshPathConfigs.h +++ b/include/settings/MeshPathConfigs.h @@ -15,6 +15,8 @@ struct MeshPathConfigs { GCodePathConfig inset0_config{}; GCodePathConfig insetX_config{}; + GCodePathConfig inset0_roofing_config{}; + GCodePathConfig insetX_roofing_config{}; GCodePathConfig bridge_inset0_config{}; GCodePathConfig bridge_insetX_config{}; GCodePathConfig skin_config{}; diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fe5ebfcc08..5cde325a69 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -21,6 +21,8 @@ #include "utils/math.h" #include "utils/orderOptimizer.h" +#include +#include #include #include @@ -2392,6 +2394,96 @@ bool FffGcodeWriter::processInsets( } else { + // for layers that (partially) do not have any layers above we apply the roofing configuration + auto use_roofing_config = [&part, &mesh, &gcode_layer](){ + const auto getOutlineOnLayer = [mesh](const SliceLayerPart& part_here, const LayerIndex layer2_nr) -> Polygons + { + Polygons result; + if (layer2_nr >= static_cast(mesh.layers.size())) + { + return result; + } + const SliceLayer& layer2 = mesh.layers[layer2_nr]; + for (const SliceLayerPart& part2 : layer2.parts) + { + if (part_here.boundaryBox.hit(part2.boundaryBox)) + { + result.add(part2.outline); + } + } + return result; + }; + + const auto filled_area_above = [&getOutlineOnLayer, &part, &mesh, &gcode_layer]() -> Polygons + { + const size_t roofing_layer_count = std::min(mesh.settings.get("roofing_layer_count"), mesh.settings.get("top_layers")); + const bool no_small_gaps_heuristic = mesh.settings.get("skin_no_small_gaps_heuristic"); + const int layer_nr = gcode_layer.getLayerNr(); + auto filled_area_above = getOutlineOnLayer(part, layer_nr + roofing_layer_count); + if (! no_small_gaps_heuristic) + { + for (int layer_nr_above = layer_nr + 1; layer_nr_above < layer_nr + roofing_layer_count; layer_nr_above++) + { + Polygons outlines_above = getOutlineOnLayer(part, layer_nr_above); + filled_area_above = filled_area_above.intersection(outlines_above); + } + } + if (layer_nr > 0) + { + // if the skin has air below it then cutting it into regions could cause a region + // to be wholely or partly above air and it may not be printable so restrict + // the regions that have air above (the visible regions) to not include any area that + // has air below (fixes https://github.com/Ultimaker/Cura/issues/2656) + + // set air_below to the skin area for the current layer that has air below it + Polygons air_below = getOutlineOnLayer(part, layer_nr).difference(getOutlineOnLayer(part, layer_nr - 1)); + + if (! air_below.empty()) + { + // add the polygons that have air below to the no air above polygons + filled_area_above = filled_area_above.unionPolygons(air_below); + } + } + + return filled_area_above; + }(); + + if (filled_area_above.empty()) + { + return true; + } + + const auto point_view = ranges::views::transform([](auto extrusion_junction) { return extrusion_junction.p; }); + + for (const auto& path: part.wall_toolpaths) + { + for (const auto& wall: path) + { + for (const auto& p : wall | point_view) + { + if (!filled_area_above.inside(p)) + { + return true; + } + } + + for (const auto& window : wall | point_view | ranges::views::sliding(2)) + { + auto p0 = window[0]; + auto p1 = window[1]; + if (PolygonUtils::polygonCollidesWithLineSegment(filled_area_above, p0, p1)) + { + return true; + } + } + } + } + return false; + }(); + + const GCodePathConfig& inset0_config = use_roofing_config ? mesh_config.inset0_roofing_config : mesh_config.inset0_config; + const GCodePathConfig& insetX_config = use_roofing_config ? mesh_config.insetX_roofing_config : mesh_config.insetX_config; + // Main case: Optimize the insets with the InsetOrderOptimizer. const coord_t wall_x_wipe_dist = 0; const ZSeamConfig z_seam_config( @@ -2405,8 +2497,8 @@ bool FffGcodeWriter::processInsets( gcode_layer, mesh.settings, extruder_nr, - mesh_config.inset0_config, - mesh_config.insetX_config, + inset0_config, + insetX_config, mesh_config.bridge_inset0_config, mesh_config.bridge_insetX_config, mesh.settings.get("travel_retract_before_outer_wall"), diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index 942084a231..a89b444bd3 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -28,6 +28,32 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), .acceleration = mesh.settings.get("acceleration_wall_x"), .jerk = mesh.settings.get("jerk_wall_x") } } + , inset0_roofing_config + { + .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { + .speed = mesh.settings.get("speed_wall_0_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), + .jerk = mesh.settings.get("jerk_wall_0_roofing") + } + } + , insetX_roofing_config + { + .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { + .speed = mesh.settings.get("speed_wall_x_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), + .jerk = mesh.settings.get("jerk_wall_x_roofing") + } + } , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") From 08dd09abf6ef354c5581fc68be8c2943ce3e06bd Mon Sep 17 00:00:00 2001 From: casperlamboo Date: Thu, 5 Oct 2023 10:47:34 +0000 Subject: [PATCH 15/30] Applied clang-format. --- src/FffGcodeWriter.cpp | 15 +++++++---- src/settings/MeshPathConfigs.cpp | 46 ++++++++++++++------------------ 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index 5cde325a69..1c592da8ad 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -2395,7 +2395,8 @@ bool FffGcodeWriter::processInsets( else { // for layers that (partially) do not have any layers above we apply the roofing configuration - auto use_roofing_config = [&part, &mesh, &gcode_layer](){ + auto use_roofing_config = [&part, &mesh, &gcode_layer]() + { const auto getOutlineOnLayer = [mesh](const SliceLayerPart& part_here, const LayerIndex layer2_nr) -> Polygons { Polygons result; @@ -2453,15 +2454,19 @@ bool FffGcodeWriter::processInsets( return true; } - const auto point_view = ranges::views::transform([](auto extrusion_junction) { return extrusion_junction.p; }); + const auto point_view = ranges::views::transform( + [](auto extrusion_junction) + { + return extrusion_junction.p; + }); - for (const auto& path: part.wall_toolpaths) + for (const auto& path : part.wall_toolpaths) { - for (const auto& wall: path) + for (const auto& wall : path) { for (const auto& p : wall | point_view) { - if (!filled_area_above.inside(p)) + if (! filled_area_above.inside(p)) { return true; } diff --git a/src/settings/MeshPathConfigs.cpp b/src/settings/MeshPathConfigs.cpp index a89b444bd3..aa71d89e07 100644 --- a/src/settings/MeshPathConfigs.cpp +++ b/src/settings/MeshPathConfigs.cpp @@ -28,32 +28,26 @@ MeshPathConfigs::MeshPathConfigs(const SliceMeshStorage& mesh, const coord_t lay .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x"), .acceleration = mesh.settings.get("acceleration_wall_x"), .jerk = mesh.settings.get("jerk_wall_x") } } - , inset0_roofing_config - { - .type = PrintFeatureType::OuterWall, - .line_width = static_cast( - mesh.settings.get("wall_line_width_0") * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), - .layer_thickness = layer_thickness, - .flow = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), - .speed_derivatives = { - .speed = mesh.settings.get("speed_wall_0_roofing"), - .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), - .jerk = mesh.settings.get("jerk_wall_0_roofing") - } - } - , insetX_roofing_config - { - .type = PrintFeatureType::OuterWall, - .line_width = static_cast( - mesh.settings.get("wall_line_width_x") * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), - .layer_thickness = layer_thickness, - .flow = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), - .speed_derivatives = { - .speed = mesh.settings.get("speed_wall_x_roofing"), - .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), - .jerk = mesh.settings.get("jerk_wall_x_roofing") - } - } + , inset0_roofing_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_0") + * line_width_factor_per_extruder[mesh.settings.get("wall_0_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow + = mesh.settings.get("wall_0_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_0_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_0_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_0_roofing"), + .jerk = mesh.settings.get("jerk_wall_0_roofing") } } + , insetX_roofing_config{ .type = PrintFeatureType::OuterWall, + .line_width = static_cast( + mesh.settings.get("wall_line_width_x") + * line_width_factor_per_extruder[mesh.settings.get("wall_x_extruder_nr").extruder_nr]), + .layer_thickness = layer_thickness, + .flow + = mesh.settings.get("wall_x_material_flow_roofing") * (layer_nr == 0 ? mesh.settings.get("wall_x_material_flow_layer_0") : Ratio{ 1.0 }), + .speed_derivatives = { .speed = mesh.settings.get("speed_wall_x_roofing"), + .acceleration = mesh.settings.get("acceleration_wall_x_roofing"), + .jerk = mesh.settings.get("jerk_wall_x_roofing") } } , bridge_inset0_config{ .type = PrintFeatureType::OuterWall, .line_width = static_cast( mesh.settings.get("wall_line_width_0") From d24323d14ba1ed4f8d90e17cf35956ca8a575b48 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Thu, 5 Oct 2023 15:17:34 +0200 Subject: [PATCH 16/30] the length of brim is correctly calculated in case of no adhesion CURA-11097 --- src/FffGcodeWriter.cpp | 1 - src/SkirtBrim.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fe5ebfcc08..300ea26147 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -3050,7 +3050,6 @@ bool FffGcodeWriter::processSupportInfill(const SliceDataStorage& storage, Layer } island_order_optimizer.optimize(); - const auto support_brim_line_count = infill_extruder.settings.get("support_brim_line_count"); const auto support_connect_zigzags = infill_extruder.settings.get("support_connect_zigzags"); const auto support_structure = infill_extruder.settings.get("support_structure"); const Point infill_origin; diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 6aed96d7fe..2c1c134d3c 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); - const coord_t length = skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE)? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 65ad30f6630b5d65e4e4905dae5c96eee57ac79b Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Thu, 5 Oct 2023 13:18:20 +0000 Subject: [PATCH 17/30] Applied clang-format. --- src/SkirtBrim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 2c1c134d3c..98992c67d9 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE)? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 54389a88290b86aacc9c162ca1e3339f6a0edd1a Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Thu, 5 Oct 2023 17:18:08 +0200 Subject: [PATCH 18/30] Better version of the corner angle computation (to be optimized) CURA-11100 --- include/PathOrderOptimizer.h | 114 +++++++++++++++++------------------ src/FffGcodeWriter.cpp | 2 +- 2 files changed, 56 insertions(+), 60 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 464810cefa..b269b1e063 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -136,7 +136,7 @@ class PathOrderOptimizer * This reorders the \ref paths field and fills their starting vertices and * directions. */ - void optimize() + void optimize(bool precompute_start = true) { if(paths.empty()) { @@ -188,7 +188,7 @@ class PathOrderOptimizer //For some Z seam types the start position can be pre-computed. //This is faster since we don't need to re-compute the start position at each step then. - const bool precompute_start = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; + precompute_start &= seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; if(precompute_start) { for(auto& path : paths) @@ -656,13 +656,13 @@ class PathOrderOptimizer case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: if(corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. { - score -= (-corner_angle + 1.0) * corner_shift; + score += corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: if(corner_angle > 0) // Indeed a convex corner? { - score -= (corner_angle + 1.0) * corner_shift; + score -= corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: @@ -707,6 +707,43 @@ class PathOrderOptimizer return best_i; } + Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance) + { + assert(distance); + + const Point here_pos = (*path.converted)[here]; + int direction = distance > 0 ? 1 : -1; + distance = std::abs(distance); + + coord_t actual_distance = 0; + int actual_delta = 0; + + Point prev_pos = here_pos; + Point next_pos; + while(actual_distance < distance) + { + actual_delta += direction; + next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + actual_distance += vSize(next_pos - prev_pos); + prev_pos = next_pos; + } + + if(actual_distance > distance) // Which is veeeery likely + { + prev_pos = (*path.converted)[(here + actual_delta -direction + path.converted->size()) % path.converted->size()]; + + Point vector = next_pos - prev_pos; + coord_t vector_size = vSize(vector); + Point unit_vector = (vector * 1000) / vector_size; + Point vector_delta = unit_vector * (vector_size - (actual_distance - distance)); + return prev_pos + vector_delta / 1000; + } + else + { + return next_pos; + } + } + /*! * Some models have very sharp corners, but also have a high resolution. If a sharp corner * consists of many points each point individual might have a shallow corner, but the @@ -722,68 +759,27 @@ class PathOrderOptimizer */ float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) { - // If the edge length becomes too small we cannot accurately calculate the angle - // define a minimum edge length, so we don't get deviant values in the angle calculations - constexpr coord_t min_edge_length = 10; - constexpr coord_t min_edge_length2 = min_edge_length * min_edge_length; + static constexpr coord_t distance_step = 2000; + static constexpr coord_t max_distance = 5000; - const int offset_index = i % path.converted->size(); - Point here = (*path.converted)[offset_index]; + const Point here = (*path.converted)[i]; - const std::function find_neighbour_point = [&offset_index, &path](const int direction, const Point& here) - { - int offset_index_ = offset_index; - Point neighbour; - do - { - offset_index_ = (offset_index_ + path.converted->size() + direction) % path.converted->size(); - neighbour = (*path.converted)[offset_index_]; - } - while (vSize2(here - neighbour) < min_edge_length2 && offset_index_ != offset_index); // find previous point that is at least min_edge_length units away from here - return neighbour; - }; - - const std::function iterate_to_previous_point = [&find_neighbour_point](Point& previous_, Point& here_, Point& next_) - { - const auto dist = vSize(here_ - next_); - next_ = here_; - here_ = previous_; - previous_ = find_neighbour_point(-1, here_); - return dist; - }; - Point previous = find_neighbour_point(-1, here); + float angle = 0.0; + int computed_angles = 0; - const std::function iterate_to_next_point = [&find_neighbour_point](Point& previous_, Point& here_, Point& next_) + for(coord_t distance = distance_step ; distance <= max_distance ; distance += distance_step) { - const auto dist = vSize(here_ - previous_); - previous_ = here_; - here_ = next_; - next_ = find_neighbour_point(1, here_); - return dist; - }; - Point next = find_neighbour_point(1, here); + Point next = findNeighbourPoint(path, i, distance); + Point previous = findNeighbourPoint(path, i, -distance); - float corner_angle = LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - - for (const auto& iterate_func : {iterate_to_previous_point, iterate_to_next_point}) - { - Point next_ = next; - Point here_ = here; - Point previous_ = previous; - for - ( - coord_t distance_to_query = iterate_func(previous_, here_, next_); - distance_to_query < angle_query_distance && here_ != here; - distance_to_query += iterate_func(previous_, here_, next_) - ) - { - // angles further away from the query point are weighted less - const float angle_weight = 1.0 - pow(distance_to_query / angle_query_distance, fall_off_strength); - corner_angle += (LinearAlg2D::getAngleLeft(previous_, here_, next_) - M_PI) * angle_weight; - } + angle += LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; + computed_angles++; } - return corner_angle / M_PI; // Limit angle between -1 and 1. + angle /= computed_angles; + angle /= M_PI; + + return angle; } /*! diff --git a/src/FffGcodeWriter.cpp b/src/FffGcodeWriter.cpp index fe5ebfcc08..07f961484e 100644 --- a/src/FffGcodeWriter.cpp +++ b/src/FffGcodeWriter.cpp @@ -1493,7 +1493,7 @@ void FffGcodeWriter::addMeshLayerToGCode( { part_order_optimizer.addPolygon(&part); } - part_order_optimizer.optimize(); + part_order_optimizer.optimize(false); for (const PathOrdering& path : part_order_optimizer.paths) { addMeshPartToGCode(storage, mesh, extruder_nr, mesh_config, *path.vertices, gcode_layer); From 317dab031139972d9f07b2b6d11b9a2252c5409e Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Thu, 5 Oct 2023 15:18:51 +0000 Subject: [PATCH 19/30] Applied clang-format. --- include/PathOrderOptimizer.h | 302 +++++++++++++++++++---------------- 1 file changed, 160 insertions(+), 142 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index b269b1e063..939baf6b21 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -4,9 +4,6 @@ #ifndef PATHORDEROPTIMIZER_H #define PATHORDEROPTIMIZER_H -#include -#include - #include "InsetOrderOptimizer.h" // for makeOrderIncludeTransitive #include "PathOrdering.h" #include "pathPlanning/CombPath.h" //To calculate the combing distance if we want to use combing. @@ -16,12 +13,16 @@ #include "utils/linearAlg2D.h" //To find the angle of corners to hide seams. #include "utils/polygonUtils.h" #include "utils/views/dfs.h" -#include -#include + #include #include -#include #include +#include +#include +#include +#include + +#include namespace cura { @@ -99,10 +100,17 @@ class PathOrderOptimizer * it into a polygon. * \param combing_boundary Boundary to avoid when making travel moves. */ - PathOrderOptimizer(const Point start_point, const ZSeamConfig seam_config = ZSeamConfig(), const bool detect_loops = false, const Polygons* combing_boundary = nullptr, const bool reverse_direction = false, const std::unordered_multimap& order_requirements = no_order_requirements, const bool group_outer_walls = false) + PathOrderOptimizer( + const Point start_point, + const ZSeamConfig seam_config = ZSeamConfig(), + const bool detect_loops = false, + const Polygons* combing_boundary = nullptr, + const bool reverse_direction = false, + const std::unordered_multimap& order_requirements = no_order_requirements, + const bool group_outer_walls = false) : start_point(start_point) , seam_config(seam_config) - , combing_boundary((combing_boundary != nullptr && !combing_boundary->empty()) ? combing_boundary : nullptr) + , combing_boundary((combing_boundary != nullptr && ! combing_boundary->empty()) ? combing_boundary : nullptr) , detect_loops(detect_loops) , reverse_direction(reverse_direction) , order_requirements(&order_requirements) @@ -138,70 +146,70 @@ class PathOrderOptimizer */ void optimize(bool precompute_start = true) { - if(paths.empty()) + if (paths.empty()) { return; } - //Get the vertex data and store it in the paths. - for(auto& path : paths) + // Get the vertex data and store it in the paths. + for (auto& path : paths) { path.converted = path.getVertexData(); vertices_to_paths.emplace(path.vertices, &path); } - //If necessary, check polylines to see if they are actually polygons. - if(detect_loops) + // If necessary, check polylines to see if they are actually polygons. + if (detect_loops) { - for(auto& path : paths) + for (auto& path : paths) { - if(!path.is_closed) + if (! path.is_closed) { - //If we want to detect chains, first check if some of the polylines are secretly polygons. - path.is_closed = isLoopingPolyline(path); //If it is, we'll set the seam position correctly later. + // If we want to detect chains, first check if some of the polylines are secretly polygons. + path.is_closed = isLoopingPolyline(path); // If it is, we'll set the seam position correctly later. } } } - - //Add all vertices to a bucket grid so that we can find nearby endpoints quickly. + + // Add all vertices to a bucket grid so that we can find nearby endpoints quickly. const coord_t snap_radius = 10_mu; // 0.01mm grid cells. Chaining only needs to consider polylines which are next to each other. SparsePointGridInclusive line_bucket_grid(snap_radius); - for(const auto& [i, path]: paths | ranges::views::enumerate) + for (const auto& [i, path] : paths | ranges::views::enumerate) { if (path.converted->empty()) { continue; } - if(path.is_closed) + if (path.is_closed) { - for(const Point& point : *path.converted) + for (const Point& point : *path.converted) { - line_bucket_grid.insert(point, i); //Store by index so that we can also mark them down in the `picked` vector. + line_bucket_grid.insert(point, i); // Store by index so that we can also mark them down in the `picked` vector. } } - else //For polylines, only insert the endpoints. Those are the only places we can start from so the only relevant vertices to be near to. + else // For polylines, only insert the endpoints. Those are the only places we can start from so the only relevant vertices to be near to. { line_bucket_grid.insert(path.converted->front(), i); line_bucket_grid.insert(path.converted->back(), i); } } - //For some Z seam types the start position can be pre-computed. - //This is faster since we don't need to re-compute the start position at each step then. + // For some Z seam types the start position can be pre-computed. + // This is faster since we don't need to re-compute the start position at each step then. precompute_start &= seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; - if(precompute_start) + if (precompute_start) { - for(auto& path : paths) + for (auto& path : paths) { - if(!path.is_closed || path.converted->empty()) + if (! path.is_closed || path.converted->empty()) { - continue; //Can't pre-compute the seam for open polylines since they're at the endpoint nearest to the current position. + continue; // Can't pre-compute the seam for open polylines since they're at the endpoint nearest to the current position. } path.start_vertex = findStartLocation(path, seam_config.pos); } } - std::vector optimized_order; //To store our result in. At the end we'll std::swap. + std::vector optimized_order; // To store our result in. At the end we'll std::swap. if (order_requirements->empty()) { @@ -213,9 +221,9 @@ class PathOrderOptimizer } - if(reverse_direction && order_requirements->empty()) + if (reverse_direction && order_requirements->empty()) { - std::vector reversed = reverseOrderPaths(optimized_order); //Reverse-insert the optimized order, to invert the ordering. + std::vector reversed = reverseOrderPaths(optimized_order); // Reverse-insert the optimized order, to invert the ordering. std::swap(reversed, paths); } else @@ -225,6 +233,7 @@ class PathOrderOptimizer combing_grid.reset(); } + protected: /*! * If \ref detect_loops is enabled, endpoints of polylines that are closer @@ -276,18 +285,24 @@ class PathOrderOptimizer std::vector getOptimizedOrder(SparsePointGridInclusive line_bucket_grid, size_t snap_radius) { - std::vector optimized_order; //To store our result in. + std::vector optimized_order; // To store our result in. Point current_position = start_point; - std::unordered_map picked(paths.size()); //Fixed size boolean flag for whether each path is already in the optimized vector. + std::unordered_map picked(paths.size()); // Fixed size boolean flag for whether each path is already in the optimized vector. - auto isPicked = [&picked](OrderablePath* c) { return picked[c]; }; - auto notPicked = [&picked](OrderablePath* c) { return !picked[c]; }; + auto isPicked = [&picked](OrderablePath* c) + { + return picked[c]; + }; + auto notPicked = [&picked](OrderablePath* c) + { + return ! picked[c]; + }; - while(optimized_order.size() < paths.size()) + while (optimized_order.size() < paths.size()) { - //Use bucket grid to find paths within snap_radius + // Use bucket grid to find paths within snap_radius std::vector nearby_candidates; for (const auto i : line_bucket_grid.getNearbyVals(current_position, snap_radius)) { @@ -296,14 +311,14 @@ class PathOrderOptimizer std::vector available_candidates; available_candidates.reserve(nearby_candidates.size()); - for(auto candidate : nearby_candidates | ranges::views::filter(notPicked)) + for (auto candidate : nearby_candidates | ranges::views::filter(notPicked)) { available_candidates.push_back(candidate); } - if(available_candidates.empty()) // We need to broaden our search through all candidates + if (available_candidates.empty()) // We need to broaden our search through all candidates { - for(auto path : paths | ranges::views::addressof | ranges::views::filter(notPicked)) + for (auto path : paths | ranges::views::addressof | ranges::views::filter(notPicked)) { available_candidates.push_back(path); } @@ -315,15 +330,15 @@ class PathOrderOptimizer optimized_order.push_back(*best_path); picked[best_path] = true; - if(!best_path->converted->empty()) //If all paths were empty, the best path is still empty. We don't upate the current position then. + if (! best_path->converted->empty()) // If all paths were empty, the best path is still empty. We don't upate the current position then. { - if(best_path->is_closed) + if (best_path->is_closed) { - current_position = (*best_path->converted)[best_path->start_vertex]; //We end where we started. + current_position = (*best_path->converted)[best_path->start_vertex]; // We end where we started. } else { - //Pick the other end from where we started. + // Pick the other end from where we started. current_position = best_path->start_vertex == 0 ? best_path->converted->back() : best_path->converted->front(); } } @@ -332,9 +347,10 @@ class PathOrderOptimizer return optimized_order; } - std::vector getOptimizerOrderWithConstraints(SparsePointGridInclusive line_bucket_grid, size_t snap_radius, const std::unordered_multimap& order_requirements) + std::vector + getOptimizerOrderWithConstraints(SparsePointGridInclusive line_bucket_grid, size_t snap_radius, const std::unordered_multimap& order_requirements) { - std::vector optimized_order; //To store our result in. + std::vector optimized_order; // To store our result in. // initialize the roots set with all possible nodes std::unordered_set roots; @@ -358,8 +374,8 @@ class PathOrderOptimizer std::unordered_set visited; Point current_position = start_point; - std::function(const Path, const std::unordered_multimap&)> get_neighbours = - [current_position, this](const Path current_node, const std::unordered_multimap& graph) + std::function(const Path, const std::unordered_multimap&)> get_neighbours + = [current_position, this](const Path current_node, const std::unordered_multimap& graph) { std::vector order; // Output order to traverse neighbors @@ -382,13 +398,13 @@ class PathOrderOptimizer // update local_current_position auto path = vertices_to_paths[best_candidate]; - if(path->is_closed) + if (path->is_closed) { - local_current_position = (*path->converted)[path->start_vertex]; //We end where we started. + local_current_position = (*path->converted)[path->start_vertex]; // We end where we started. } else { - //Pick the other end from where we started. + // Pick the other end from where we started. local_current_position = path->start_vertex == 0 ? path->converted->back() : path->converted->front(); } } @@ -396,34 +412,33 @@ class PathOrderOptimizer return order; }; - const std::function handle_node = - [¤t_position, &optimized_order, this] - (const Path current_node, const std::nullptr_t _state) + const std::function handle_node + = [¤t_position, &optimized_order, this](const Path current_node, const std::nullptr_t _state) + { + // We should make map from node <-> path for this stuff + for (auto& path : paths) { - // We should make map from node <-> path for this stuff - for (auto& path : paths) + if (path.vertices == current_node) { - if (path.vertices == current_node) + if (path.is_closed) { - if(path.is_closed) - { - current_position = (*path.converted)[path.start_vertex]; //We end where we started. - } - else - { - //Pick the other end from where we started. - current_position = path.start_vertex == 0 ? path.converted->back() : path.converted->front(); - } - - // Add to optimized order - optimized_order.push_back(path); - - break; + current_position = (*path.converted)[path.start_vertex]; // We end where we started. } + else + { + // Pick the other end from where we started. + current_position = path.start_vertex == 0 ? path.converted->back() : path.converted->front(); + } + + // Add to optimized order + optimized_order.push_back(path); + + break; } + } - return nullptr; - }; + return nullptr; + }; if (group_outer_walls) { @@ -482,7 +497,7 @@ class PathOrderOptimizer } else { - while (!roots.empty()) + while (! roots.empty()) { Path root = findClosestPathVertices(current_position, roots); roots.erase(root); @@ -497,13 +512,13 @@ class PathOrderOptimizer std::vector reverseOrderPaths(std::vector pathsOrderPaths) { std::vector reversed; - //Don't replace with swap, assign or insert. They require functions that we can't implement for all template arguments for Path. + // Don't replace with swap, assign or insert. They require functions that we can't implement for all template arguments for Path. reversed.reserve(pathsOrderPaths.size()); - for(auto& path: pathsOrderPaths | ranges::views::reverse) + for (auto& path : pathsOrderPaths | ranges::views::reverse) { reversed.push_back(path); - reversed.back().backwards = !reversed.back().backwards; - if(!reversed.back().is_closed) + reversed.back().backwards = ! reversed.back().backwards; + if (! reversed.back().is_closed) { reversed.back().start_vertex = reversed.back().converted->size() - 1 - reversed.back().start_vertex; } @@ -530,33 +545,35 @@ class PathOrderOptimizer coord_t best_distance2 = std::numeric_limits::max(); OrderablePath* best_candidate = 0; - for(OrderablePath* path : candidate_paths) + for (OrderablePath* path : candidate_paths) { - if(path->converted->empty()) //No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. + if (path->converted->empty()) // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end. { - if(best_distance2 == std::numeric_limits::max()) + if (best_distance2 == std::numeric_limits::max()) { best_candidate = path; } continue; } - const bool precompute_start = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; - if(!path->is_closed || !precompute_start) //Find the start location unless we've already precomputed it. + const bool precompute_start + = seam_config.type == EZSeamType::RANDOM || seam_config.type == EZSeamType::USER_SPECIFIED || seam_config.type == EZSeamType::SHARPEST_CORNER; + if (! path->is_closed || ! precompute_start) // Find the start location unless we've already precomputed it. { path->start_vertex = findStartLocation(*path, start_position); - if(!path->is_closed) //Open polylines start at vertex 0 or vertex N-1. Indicate that they should be reversed if they start at N-1. + if (! path->is_closed) // Open polylines start at vertex 0 or vertex N-1. Indicate that they should be reversed if they start at N-1. { path->backwards = path->start_vertex > 0; } } const Point candidate_position = (*path->converted)[path->start_vertex]; coord_t distance2 = getDirectDistance(start_position, candidate_position); - if(distance2 < best_distance2 && combing_boundary) //If direct distance is longer than best combing distance, the combing distance can never be better, so only compute combing if necessary. + if (distance2 < best_distance2 + && combing_boundary) // If direct distance is longer than best combing distance, the combing distance can never be better, so only compute combing if necessary. { distance2 = getCombingDistance(start_position, candidate_position); } - if(distance2 < best_distance2) //Closer than the best candidate so far. + if (distance2 < best_distance2) // Closer than the best candidate so far. { best_candidate = path; best_distance2 = distance2; @@ -589,30 +606,31 @@ class PathOrderOptimizer * applicable. * \param is_closed Whether the polygon is closed (a polygon) or not * (a polyline). If the path is not closed, it will choose between the two - * endpoints rather than + * endpoints rather than * \return An index to a vertex in that path where printing must start. */ size_t findStartLocation(const OrderablePath& path, const Point& target_pos) { - if(!path.is_closed) + if (! path.is_closed) { - //For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. - const coord_t back_distance = (combing_boundary == nullptr) - ? getDirectDistance(path.converted->back(), target_pos) - : getCombingDistance(path.converted->back(), target_pos); - if(back_distance < getDirectDistance(path.converted->front(), target_pos) || (combing_boundary && back_distance < getCombingDistance(path.converted->front(), target_pos))) //Lazy or: Only compute combing distance if direct distance is closer. + // For polylines, the seam settings are not applicable. Simply choose the position closest to target_pos then. + const coord_t back_distance + = (combing_boundary == nullptr) ? getDirectDistance(path.converted->back(), target_pos) : getCombingDistance(path.converted->back(), target_pos); + if (back_distance < getDirectDistance(path.converted->front(), target_pos) + || (combing_boundary + && back_distance < getCombingDistance(path.converted->front(), target_pos))) // Lazy or: Only compute combing distance if direct distance is closer. { - return path.converted->size() - 1; //Back end is closer. + return path.converted->size() - 1; // Back end is closer. } else { - return 0; //Front end is closer. + return 0; // Front end is closer. } } - //Rest of the function only deals with (closed) polygons. We need to be able to find the seam location of those polygons. + // Rest of the function only deals with (closed) polygons. We need to be able to find the seam location of those polygons. - if(seam_config.type == EZSeamType::RANDOM) + if (seam_config.type == EZSeamType::RANDOM) { size_t vert = getRandomPointInPolygon(*path.converted); return vert; @@ -620,14 +638,14 @@ class PathOrderOptimizer size_t best_i; float best_score = std::numeric_limits::infinity(); - for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - //For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) - ? getDirectDistance(here, target_pos) - : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + // For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) + ? MM2INT(10) + : vSize2(here - target_pos); float corner_angle = cornerAngle(path, i); // angles < 0 are concave (left turning) @@ -650,30 +668,30 @@ class PathOrderOptimizer } float score = score_distance; - switch(seam_config.corner_pref) + switch (seam_config.corner_pref) { default: case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: - if(corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. + if (corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. { score += corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: - if(corner_angle > 0) // Indeed a convex corner? + if (corner_angle > 0) // Indeed a convex corner? { score -= corner_angle * corner_shift; } break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: - score -= std::abs(corner_angle) * corner_shift; //Still give sharper corners more advantage. + score -= std::abs(corner_angle) * corner_shift; // Still give sharper corners more advantage. break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE: break; - case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED: //Give sharper corners some advantage, but sharper concave corners even more. + case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_WEIGHTED: // Give sharper corners some advantage, but sharper concave corners even more. { float score_corner = std::abs(corner_angle) * corner_shift; - if(corner_angle < 0) //Concave corner. + if (corner_angle < 0) // Concave corner. { score_corner *= 2; } @@ -683,7 +701,7 @@ class PathOrderOptimizer } constexpr float EPSILON = 25.0; - if(std::abs(best_score - score) <= EPSILON) + if (std::abs(best_score - score) <= EPSILON) { // add breaker for two candidate starting location with similar score // if we don't do this then we (can) get an un-even seam @@ -691,13 +709,13 @@ class PathOrderOptimizer // if x-coord for both points are equal then break ties by // favouring points with lower y-coord const Point& best_point = (*path.converted)[best_i]; - if(std::abs(here.Y - best_point.Y) <= EPSILON ? best_point.X < here.X : best_point.Y < here.Y) + if (std::abs(here.Y - best_point.Y) <= EPSILON ? best_point.X < here.X : best_point.Y < here.Y) { best_score = std::min(best_score, score); best_i = i; } } - else if(score < best_score) + else if (score < best_score) { best_i = i; best_score = score; @@ -720,7 +738,7 @@ class PathOrderOptimizer Point prev_pos = here_pos; Point next_pos; - while(actual_distance < distance) + while (actual_distance < distance) { actual_delta += direction; next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; @@ -728,9 +746,9 @@ class PathOrderOptimizer prev_pos = next_pos; } - if(actual_distance > distance) // Which is veeeery likely + if (actual_distance > distance) // Which is veeeery likely { - prev_pos = (*path.converted)[(here + actual_delta -direction + path.converted->size()) % path.converted->size()]; + prev_pos = (*path.converted)[(here + actual_delta - direction + path.converted->size()) % path.converted->size()]; Point vector = next_pos - prev_pos; coord_t vector_size = vSize(vector); @@ -745,18 +763,18 @@ class PathOrderOptimizer } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from all points within angle_query_distance of the query point. Angles closer - * to the current point are weighted more towards the total angle then points further away. - * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength - * \param path The vertex data of a path - * \param i index of the query point - * \param angle_query_distance query range (default to 0.1mm) - * \param fall_off_strength fall of strength of the angle weight - * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance - */ + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from all points within angle_query_distance of the query point. Angles closer + * to the current point are weighted more towards the total angle then points further away. + * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength + * \param path The vertex data of a path + * \param i index of the query point + * \param angle_query_distance query range (default to 0.1mm) + * \param fall_off_strength fall of strength of the angle weight + * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance + */ float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) { static constexpr coord_t distance_step = 2000; @@ -767,7 +785,7 @@ class PathOrderOptimizer float angle = 0.0; int computed_angles = 0; - for(coord_t distance = distance_step ; distance <= max_distance ; distance += distance_step) + for (coord_t distance = distance_step; distance <= max_distance; distance += distance_step) { Point next = findNeighbourPoint(path, i, distance); Point previous = findNeighbourPoint(path, i, -distance); @@ -806,11 +824,11 @@ class PathOrderOptimizer */ coord_t getCombingDistance(const Point& a, const Point& b) { - if(!PolygonUtils::polygonCollidesWithLineSegment(*combing_boundary, a, b)) + if (! PolygonUtils::polygonCollidesWithLineSegment(*combing_boundary, a, b)) { - return getDirectDistance(a, b); //No collision with any line. Just compute the direct distance then. + return getDirectDistance(a, b); // No collision with any line. Just compute the direct distance then. } - if(paths.size() > 100) + if (paths.size() > 100) { /* If we have many paths to optimize the order for, this combing calculation can become very expensive. Instead, penalize travels @@ -818,13 +836,13 @@ class PathOrderOptimizer return getDirectDistance(a, b) * 5; } - if(combing_grid == nullptr) + if (combing_grid == nullptr) { - constexpr coord_t grid_size = 2000; //2mm grid cells. Smaller will use more memory, but reduce chance of unnecessary collision checks. + constexpr coord_t grid_size = 2000; // 2mm grid cells. Smaller will use more memory, but reduce chance of unnecessary collision checks. combing_grid = PolygonUtils::createLocToLineGrid(*combing_boundary, grid_size); } - CombPath comb_path; //Output variable. + CombPath comb_path; // Output variable. constexpr coord_t rounding_error = -25; constexpr coord_t tiny_travel_threshold = 0; constexpr bool fail_on_unavoidable_obstacles = false; @@ -832,12 +850,12 @@ class PathOrderOptimizer coord_t sum = 0; Point last_point = a; - for(const Point& point : comb_path) + for (const Point& point : comb_path) { sum += vSize(point - last_point); last_point = point; } - return sum * sum; //Squared distance, for fair comparison with direct distance. + return sum * sum; // Squared distance, for fair comparison with direct distance. } /*! @@ -852,7 +870,7 @@ class PathOrderOptimizer bool isLoopingPolyline(const OrderablePath& path) { - if(path.converted->empty()) + if (path.converted->empty()) { return false; } @@ -863,6 +881,6 @@ class PathOrderOptimizer template const std::unordered_multimap PathOrderOptimizer::no_order_requirements; -} //namespace cura +} // namespace cura -#endif //PATHORDEROPTIMIZER_H +#endif // PATHORDEROPTIMIZER_H From 9dbcdf80ea06fbdee18769fce327e4a524170e83 Mon Sep 17 00:00:00 2001 From: "saumya.jain" Date: Fri, 6 Oct 2023 10:54:26 +0200 Subject: [PATCH 20/30] comment for adding if statement CURA-11097 --- src/SkirtBrim.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 98992c67d9..1731714763 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -667,8 +667,8 @@ void SkirtBrim::generateSupportBrim() } storage.support_brim.add(brim_line); - - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); + // In case of adhesion::NONE length of support brim is only the length of the brims formed for the support + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From 27e3c92f3e786a30db78e37e6a1480d3e4c0f743 Mon Sep 17 00:00:00 2001 From: saumyaj3 Date: Fri, 6 Oct 2023 08:56:34 +0000 Subject: [PATCH 21/30] Applied clang-format. --- src/SkirtBrim.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SkirtBrim.cpp b/src/SkirtBrim.cpp index 1731714763..94761b0717 100644 --- a/src/SkirtBrim.cpp +++ b/src/SkirtBrim.cpp @@ -668,7 +668,7 @@ void SkirtBrim::generateSupportBrim() storage.support_brim.add(brim_line); // In case of adhesion::NONE length of support brim is only the length of the brims formed for the support - const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length: skirt_brim_length + storage.support_brim.polygonLength(); + const coord_t length = (adhesion_type == EPlatformAdhesion::NONE) ? skirt_brim_length : skirt_brim_length + storage.support_brim.polygonLength(); if (skirt_brim_number + 1 >= line_count && length > 0 && length < minimal_length) // Make brim or skirt have more lines when total length is too small. { line_count++; From a383339eae365892aa7d3cf7db3c1ddbdf537cfb Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Fri, 6 Oct 2023 13:07:14 +0200 Subject: [PATCH 22/30] Clean and optimized new corner angle computation algorithm CURA-11100 --- include/PathOrderOptimizer.h | 136 ++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 58 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 939baf6b21..108b49b61a 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -636,18 +636,35 @@ class PathOrderOptimizer return vert; } + //Precompute segments lengths because we are going to need them multiple times + std::vector segments_sizes(path.converted->size()); + coord_t total_length = 0; + for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + { + const Point &next = (*path.converted)[(i + 1) % path.converted->size()]; + coord_t segment_size = vSize(next - here); + segments_sizes[i] = segment_size; + total_length += segment_size; + } + size_t best_i; float best_score = std::numeric_limits::infinity(); for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - // For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) - ? MM2INT(10) - : vSize2(here - target_pos); + if(i == path.converted->size() - 1) + { + //The path is closed so the last point is the same as the first, don't process it twice + continue; + } - float corner_angle = cornerAngle(path, i); + //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + //For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) + ? getDirectDistance(here, target_pos) + : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + + float corner_angle = cornerAngle(path, i, segments_sizes, total_length); // angles < 0 are concave (left turning) // angles > 0 are convex (right turning) @@ -725,79 +742,82 @@ class PathOrderOptimizer return best_i; } - Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance) + /*! + * Finds a neighbour point on the path, located before or after the given reference point. The neighbour point + * is computed by travelling on the path and stopping when the distance has been reached, For example: + * |------|---------|------|--------------*---| + * H A B C N D + * In this case, H is the start point of the path and ABCD are the actual following points of the path. + * The neighbour point N is found by reaching point D then going a bit backward on the previous segment. + * This approach gets rid of the mesh actual resolution and gives a neighbour point that is on the path + * at a given physical distance. + * \param path The vertex data of a path + * \param here The starting point index + * \param distance The distance we want to travel on the path, which may be positive to go forward + * or negative to go backward + * \param segments_sizes The pre-computed sizes of the segments + * \return The position of the path a the given distance from the reference point + */ + static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector &segments_sizes) { - assert(distance); - - const Point here_pos = (*path.converted)[here]; - int direction = distance > 0 ? 1 : -1; + const int direction = distance > 0 ? 1 : -1; + const int size_delta = distance > 0 ? -1 : 0; distance = std::abs(distance); - coord_t actual_distance = 0; + // Travel on the path until we reach the distance int actual_delta = 0; - - Point prev_pos = here_pos; - Point next_pos; - while (actual_distance < distance) + coord_t travelled_distance = 0; + coord_t segment_size = 0; + while(travelled_distance < distance) { actual_delta += direction; - next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; - actual_distance += vSize(next_pos - prev_pos); - prev_pos = next_pos; + segment_size = segments_sizes[(here + actual_delta + size_delta + path.converted->size()) % path.converted->size()]; + travelled_distance += segment_size; } - if (actual_distance > distance) // Which is veeeery likely + const Point &next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + + if(travelled_distance > distance) [[likely]] { - prev_pos = (*path.converted)[(here + actual_delta - direction + path.converted->size()) % path.converted->size()]; + // We have overtaken the required distance, go backward on the last segment + int prev = (here + actual_delta -direction + path.converted->size()) % path.converted->size(); + const Point &prev_pos = (*path.converted)[prev]; - Point vector = next_pos - prev_pos; - coord_t vector_size = vSize(vector); - Point unit_vector = (vector * 1000) / vector_size; - Point vector_delta = unit_vector * (vector_size - (actual_distance - distance)); + const Point vector = next_pos - prev_pos; + const Point unit_vector = (vector * 1000) / segment_size; + const Point vector_delta = unit_vector * (segment_size - (travelled_distance - distance)); return prev_pos + vector_delta / 1000; } else { + // Luckily, the required distance stops exactly on an existing point return next_pos; } } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from all points within angle_query_distance of the query point. Angles closer - * to the current point are weighted more towards the total angle then points further away. - * The formula for the angle weight is: 1 - (distance_to_query / angle_query_distance)^fall_off_strength - * \param path The vertex data of a path - * \param i index of the query point - * \param angle_query_distance query range (default to 0.1mm) - * \param fall_off_strength fall of strength of the angle weight - * \return sum of angles of all points p in range i - angle_query_distance < p < i + angle_query_distance - */ - float cornerAngle(const OrderablePath& path, int i, const coord_t angle_query_distance = 100, const float fall_off_strength = 0.5) + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from two points within angle_query_distance of the query point, no matter + * what segment this leads us to + * \param path The vertex data of a path + * \param i index of the query point + * \param segments_sizes The pre-computed sizes of the segments + * \param total_length The path total length + * \param angle_query_distance query range (default to 1mm) + * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] + */ + static float cornerAngle(const OrderablePath& path, int i, const std::vector &segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) { - static constexpr coord_t distance_step = 2000; - static constexpr coord_t max_distance = 5000; - - const Point here = (*path.converted)[i]; - - float angle = 0.0; - int computed_angles = 0; - - for (coord_t distance = distance_step; distance <= max_distance; distance += distance_step) - { - Point next = findNeighbourPoint(path, i, distance); - Point previous = findNeighbourPoint(path, i, -distance); - - angle += LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - computed_angles++; - } + const coord_t bounded_distance = std::min(angle_query_distance, total_length / 2); + const Point &here = (*path.converted)[i]; + const Point next = findNeighbourPoint(path, i, bounded_distance, segments_sizes); + const Point previous = findNeighbourPoint(path, i, -bounded_distance, segments_sizes); - angle /= computed_angles; - angle /= M_PI; + float angle = LinearAlg2D::getAngleLeft(previous, here, next) - M_PI; - return angle; + return angle / M_PI; } /*! From 6311be84cfdccd3ba9c7815b7a325c86866a65e1 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Fri, 6 Oct 2023 11:12:44 +0000 Subject: [PATCH 23/30] Applied clang-format. --- include/PathOrderOptimizer.h | 62 ++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 108b49b61a..d8d6d9ca17 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -636,12 +636,12 @@ class PathOrderOptimizer return vert; } - //Precompute segments lengths because we are going to need them multiple times + // Precompute segments lengths because we are going to need them multiple times std::vector segments_sizes(path.converted->size()); coord_t total_length = 0; - for(const auto& [i, here]: **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - const Point &next = (*path.converted)[(i + 1) % path.converted->size()]; + const Point& next = (*path.converted)[(i + 1) % path.converted->size()]; coord_t segment_size = vSize(next - here); segments_sizes[i] = segment_size; total_length += segment_size; @@ -651,18 +651,18 @@ class PathOrderOptimizer float best_score = std::numeric_limits::infinity(); for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { - if(i == path.converted->size() - 1) + if (i == path.converted->size() - 1) { - //The path is closed so the last point is the same as the first, don't process it twice + // The path is closed so the last point is the same as the first, don't process it twice continue; } - //For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. - //For SHARPEST_CORNER, use a fixed starting score of 0. - const coord_t distance = (combing_boundary == nullptr) - ? getDirectDistance(here, target_pos) - : getCombingDistance(here, target_pos); - const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) ? MM2INT(10) : vSize2(here - target_pos); + // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. + // For SHARPEST_CORNER, use a fixed starting score of 0. + const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); + const float score_distance = (seam_config.type == EZSeamType::SHARPEST_CORNER && seam_config.corner_pref != EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_NONE) + ? MM2INT(10) + : vSize2(here - target_pos); float corner_angle = cornerAngle(path, i, segments_sizes, total_length); // angles < 0 are concave (left turning) @@ -758,7 +758,7 @@ class PathOrderOptimizer * \param segments_sizes The pre-computed sizes of the segments * \return The position of the path a the given distance from the reference point */ - static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector &segments_sizes) + static Point findNeighbourPoint(const OrderablePath& path, int here, coord_t distance, const std::vector& segments_sizes) { const int direction = distance > 0 ? 1 : -1; const int size_delta = distance > 0 ? -1 : 0; @@ -768,20 +768,20 @@ class PathOrderOptimizer int actual_delta = 0; coord_t travelled_distance = 0; coord_t segment_size = 0; - while(travelled_distance < distance) + while (travelled_distance < distance) { actual_delta += direction; segment_size = segments_sizes[(here + actual_delta + size_delta + path.converted->size()) % path.converted->size()]; travelled_distance += segment_size; } - const Point &next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; + const Point& next_pos = (*path.converted)[(here + actual_delta + path.converted->size()) % path.converted->size()]; - if(travelled_distance > distance) [[likely]] + if (travelled_distance > distance) [[likely]] { // We have overtaken the required distance, go backward on the last segment - int prev = (here + actual_delta -direction + path.converted->size()) % path.converted->size(); - const Point &prev_pos = (*path.converted)[prev]; + int prev = (here + actual_delta - direction + path.converted->size()) % path.converted->size(); + const Point& prev_pos = (*path.converted)[prev]; const Point vector = next_pos - prev_pos; const Point unit_vector = (vector * 1000) / segment_size; @@ -796,22 +796,22 @@ class PathOrderOptimizer } /*! - * Some models have very sharp corners, but also have a high resolution. If a sharp corner - * consists of many points each point individual might have a shallow corner, but the - * collective angle of all nearby points is greater. To counter this the cornerAngle is - * calculated from two points within angle_query_distance of the query point, no matter - * what segment this leads us to - * \param path The vertex data of a path - * \param i index of the query point - * \param segments_sizes The pre-computed sizes of the segments - * \param total_length The path total length - * \param angle_query_distance query range (default to 1mm) - * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] - */ - static float cornerAngle(const OrderablePath& path, int i, const std::vector &segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) + * Some models have very sharp corners, but also have a high resolution. If a sharp corner + * consists of many points each point individual might have a shallow corner, but the + * collective angle of all nearby points is greater. To counter this the cornerAngle is + * calculated from two points within angle_query_distance of the query point, no matter + * what segment this leads us to + * \param path The vertex data of a path + * \param i index of the query point + * \param segments_sizes The pre-computed sizes of the segments + * \param total_length The path total length + * \param angle_query_distance query range (default to 1mm) + * \return angle between the reference point and the two sibling points, weighed to [-1.0 ; 1.0] + */ + static float cornerAngle(const OrderablePath& path, int i, const std::vector& segments_sizes, coord_t total_length, const coord_t angle_query_distance = 1000) { const coord_t bounded_distance = std::min(angle_query_distance, total_length / 2); - const Point &here = (*path.converted)[i]; + const Point& here = (*path.converted)[i]; const Point next = findNeighbourPoint(path, i, bounded_distance, segments_sizes); const Point previous = findNeighbourPoint(path, i, -bounded_distance, segments_sizes); From c562a43aab7fb36007d7cbaa15b1471cfac37576 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 10:38:33 +0200 Subject: [PATCH 24/30] Removed debug output --- src/SkeletalTrapezoidationGraph.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/SkeletalTrapezoidationGraph.cpp b/src/SkeletalTrapezoidationGraph.cpp index ed8cc1035d..4657b8ee97 100644 --- a/src/SkeletalTrapezoidationGraph.cpp +++ b/src/SkeletalTrapezoidationGraph.cpp @@ -3,17 +3,18 @@ #include "SkeletalTrapezoidationGraph.h" -#include +#include "utils/linearAlg2D.h" +#include "utils/macros.h" #include -#include "utils/linearAlg2D.h" -#include "utils/macros.h" +#include namespace cura { -STHalfEdge::STHalfEdge(SkeletalTrapezoidationEdge data) : HalfEdge(data) +STHalfEdge::STHalfEdge(SkeletalTrapezoidationEdge data) + : HalfEdge(data) { } @@ -131,7 +132,8 @@ STHalfEdge* STHalfEdge::getNextUnconnected() return result->twin; } -STHalfEdgeNode::STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p) : HalfEdgeNode(data, p) +STHalfEdgeNode::STHalfEdgeNode(SkeletalTrapezoidationJoint data, Point p) + : HalfEdgeNode(data, p) { } @@ -223,7 +225,10 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) } }; - auto should_collapse = [snap_dist](node_t* a, node_t* b) { return shorterThen(a->p - b->p, snap_dist); }; + auto should_collapse = [snap_dist](node_t* a, node_t* b) + { + return shorterThen(a->p - b->p, snap_dist); + }; for (auto edge_it = edges.begin(); edge_it != edges.end();) { @@ -253,10 +258,6 @@ void SkeletalTrapezoidationGraph::collapseSmallEdges(coord_t snap_dist) { edge_from_3->from = quad_mid->from; edge_from_3->twin->to = quad_mid->from; - if (count > 50) - { - std::cerr << edge_from_3->from->p << " - " << edge_from_3->to->p << '\n'; - } if (++count > 1000) { break; From 00b71215d5d80f81a9b9ae4b5531beb593e4bd96 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 10:52:13 +0200 Subject: [PATCH 25/30] Fixed some edge-cases with new angle calculation CURA-11100 --- include/PathOrderOptimizer.h | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index d8d6d9ca17..ab58d30651 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -689,16 +689,12 @@ class PathOrderOptimizer { default: case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_INNER: - if (corner_angle < 0) // Indeed a concave corner? Give it some advantage over other corners. More advantage for sharper corners. - { - score += corner_angle * corner_shift; - } + // Give advantage to concave corners. More advantage for sharper corners. + score += corner_angle * corner_shift; break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_OUTER: - if (corner_angle > 0) // Indeed a convex corner? - { - score -= corner_angle * corner_shift; - } + // Give advantage to convex corners. More advantage for sharper corners. + score -= corner_angle * corner_shift; break; case EZSeamCornerPrefType::Z_SEAM_CORNER_PREF_ANY: score -= std::abs(corner_angle) * corner_shift; // Still give sharper corners more advantage. @@ -717,7 +713,7 @@ class PathOrderOptimizer } } - constexpr float EPSILON = 25.0; + constexpr float EPSILON = 5.0; if (std::abs(best_score - score) <= EPSILON) { // add breaker for two candidate starting location with similar score From edd9b398369e06a4b90a6b5de9532febcfc11954 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 15:26:34 +0200 Subject: [PATCH 26/30] Minor optimization CURA-11100 Co-authored-by: Casper Lamboo --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index ab58d30651..ce11a18b12 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -642,7 +642,7 @@ class PathOrderOptimizer for (const auto& [i, here] : **path.converted | ranges::views::enumerate) { const Point& next = (*path.converted)[(i + 1) % path.converted->size()]; - coord_t segment_size = vSize(next - here); + const coord_t segment_size = vSize(next - here); segments_sizes[i] = segment_size; total_length += segment_size; } From 0c757cb7aabc8a4cba0948e009d3848dd4d47138 Mon Sep 17 00:00:00 2001 From: Erwan MATHIEU Date: Mon, 9 Oct 2023 15:44:51 +0200 Subject: [PATCH 27/30] Applied code suggestion by @casperlamboo CURA-11100 --- include/PathOrderOptimizer.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index ce11a18b12..506e624927 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -649,14 +650,8 @@ class PathOrderOptimizer size_t best_i; float best_score = std::numeric_limits::infinity(); - for (const auto& [i, here] : **path.converted | ranges::views::enumerate) + for (const auto& [i, here] : **path.converted | ranges::views::drop_last(1) | ranges::views::enumerate) { - if (i == path.converted->size() - 1) - { - // The path is closed so the last point is the same as the first, don't process it twice - continue; - } - // For most seam types, the shortest distance matters. Not for SHARPEST_CORNER though. // For SHARPEST_CORNER, use a fixed starting score of 0. const coord_t distance = (combing_boundary == nullptr) ? getDirectDistance(here, target_pos) : getCombingDistance(here, target_pos); From 47cc9ba3419b519ea29d48b847139b5deb8a3221 Mon Sep 17 00:00:00 2001 From: wawanbreton Date: Mon, 9 Oct 2023 13:45:37 +0000 Subject: [PATCH 28/30] Applied clang-format. --- include/PathOrderOptimizer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PathOrderOptimizer.h b/include/PathOrderOptimizer.h index 506e624927..ae7591db45 100644 --- a/include/PathOrderOptimizer.h +++ b/include/PathOrderOptimizer.h @@ -17,10 +17,10 @@ #include #include #include +#include #include #include #include -#include #include #include From b3ff78c015312b6155cddafa9fb1f62fe037ad9b Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:40:09 +0200 Subject: [PATCH 29/30] pin gRPC defs to 0.1.0-beta.1 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index dea1860300..841ec2d936 100644 --- a/conanfile.py +++ b/conanfile.py @@ -85,7 +85,7 @@ def requirements(self): self.requires("arcus/5.3.0") self.requires("asio-grpc/2.6.0") self.requires("grpc/1.50.1") - self.requires("curaengine_grpc_definitions/(latest)@ultimaker/testing") + self.requires("curaengine_grpc_definitions/0.1.0-beta.1") self.requires("clipper/6.4.2") self.requires("boost/1.82.0") self.requires("rapidjson/1.1.0") From 0503a4c7a2e554bf8f2e9cb3e0e3824df0a54fa8 Mon Sep 17 00:00:00 2001 From: Jelle Spijker Date: Tue, 10 Oct 2023 10:40:52 +0200 Subject: [PATCH 30/30] set version to 5.5.0-beta.2 --- conanfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conanfile.py b/conanfile.py index 841ec2d936..2556e86c45 100644 --- a/conanfile.py +++ b/conanfile.py @@ -40,7 +40,7 @@ class CuraEngineConan(ConanFile): def set_version(self): if not self.version: - self.version = "5.5.0-beta.1" + self.version = "5.5.0-beta.2" def export_sources(self): copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder)