From 891cf532b7e35384195b6e2475afdd1a7096fa0c Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 14 Feb 2024 17:38:09 +0100 Subject: [PATCH 01/24] version 2.5.59.9 --- version.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.inc b/version.inc index 3f73727d61a..f51687352cc 100644 --- a/version.inc +++ b/version.inc @@ -9,9 +9,9 @@ set(SLIC3R_APP_KEY "Slic3r") set(SLIC3R_APP_CMD "Slic3r") # versions set(SLIC3R_VERSION "2.5") -set(SLIC3R_VERSION_FULL "2.5.59.8") +set(SLIC3R_VERSION_FULL "2.5.59.9") set(SLIC3R_BUILD_ID "${SLIC3R_APP_KEY}_${SLIC3R_VERSION_FULL}+UNKNOWN") -set(SLIC3R_RC_VERSION "2,5,59,8") +set(SLIC3R_RC_VERSION "2,5,59,9") set(SLIC3R_RC_VERSION_DOTS "${SLIC3R_VERSION_FULL}") # Same as the slicer name but for gcodeviewer From 026a63ac3819e65c33ad56375bf42b37b1a16d91 Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 15 Feb 2024 16:47:36 +0100 Subject: [PATCH 02/24] Fix some arachne problem (seams and overhangs) reproducible with model from issue 4129 --- src/libslic3r/Arachne/utils/ExtrusionLine.hpp | 7 ++ src/libslic3r/GCode/SeamPlacer.cpp | 38 +++++++-- src/libslic3r/PerimeterGenerator.cpp | 79 +++++++++++++------ 3 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/libslic3r/Arachne/utils/ExtrusionLine.hpp b/src/libslic3r/Arachne/utils/ExtrusionLine.hpp index 0b13859e207..458635d5040 100644 --- a/src/libslic3r/Arachne/utils/ExtrusionLine.hpp +++ b/src/libslic3r/Arachne/utils/ExtrusionLine.hpp @@ -224,6 +224,13 @@ static inline Slic3r::ThickPolyline to_thick_polyline(const ClipperLib_Z::Path & out.points_width.emplace_back(it->z()); } } + // Don't create 1-element polyline. + if(out.points.size() <2) + return {}; + + assert(out.points.back().coincides_with_epsilon(Point{ path.back().x(), path.back().y() })); + out.points.back() = Point{ path.back().x(), path.back().y() }; + assert(out.points.front().x() == path.front().x()); assert(out.points.front().y() == path.front().y()); assert(out.points.back().x() == path.back().x()); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 90b7fbe17dd..549d6034904 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -421,7 +421,7 @@ struct GlobalModelInfo { //Extract perimeter polygons of the given layer Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition configured_seam_preference, - std::vector &corresponding_regions_out) { + std::vector &corresponding_regions_out, PerimeterGeneratorType perimeter_type) { Polygons polygons; @@ -430,13 +430,22 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi std::vector* corresponding_regions_out; const LayerRegion* current_layer_region; SeamPosition configured_seam_preference; + PerimeterGeneratorType perimeter_type; public: bool also_overhangs = false; bool also_thin_walls = false; - PerimeterCopy(std::vector* regions_out, Polygons* polys, SeamPosition configured_seam) - : corresponding_regions_out(regions_out), configured_seam_preference(configured_seam), polygons(polys) { + PerimeterCopy(std::vector* regions_out, Polygons* polys, SeamPosition configured_seam, PerimeterGeneratorType perimeter_type) + : corresponding_regions_out(regions_out), configured_seam_preference(configured_seam), polygons(polys), perimeter_type(perimeter_type) { } virtual void default_use(const ExtrusionEntity& entity) {}; + virtual void use(const ExtrusionPath &path) override { + if (perimeter_type == PerimeterGeneratorType::Arachne && path.role() != erThinWall) { + path.polygons_covered_by_width(*polygons, SCALED_EPSILON); + while (corresponding_regions_out->size() < polygons->size()) { + corresponding_regions_out->push_back(current_layer_region); + } + } + } virtual void use(const ExtrusionLoop& loop) override { if ((configured_seam_preference == spAllRandom && !loop.paths.empty() && is_perimeter(loop.paths.front().role())) @@ -468,13 +477,27 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi } } } + virtual void use(const ExtrusionMultiPath& collection) override { + + if (perimeter_type == PerimeterGeneratorType::Arachne) { + Polygons polys; + for (const ExtrusionPath& path : collection.paths) { + path.polygons_covered_by_width(polys, SCALED_EPSILON); + } + polys = union_(polys); + append(*polygons, polys); + while (corresponding_regions_out->size() < polygons->size()) { + corresponding_regions_out->push_back(current_layer_region); + } + } + } virtual void use(const ExtrusionEntityCollection& collection) override { for (const ExtrusionEntity* entity : collection.entities()) { entity->visit(*this); } } void set_current_layer_region(const LayerRegion *set) { current_layer_region = set; } - } visitor(&corresponding_regions_out, &polygons, configured_seam_preference); + } visitor(&corresponding_regions_out, &polygons, configured_seam_preference, perimeter_type); for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities()) { @@ -504,13 +527,12 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi } } } - if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point // it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway polygons.emplace_back(std::vector { Point { 0, 0 } }); corresponding_regions_out.push_back(nullptr); } - + assert(corresponding_regions_out.size() == polygons.size()); return polygons; } @@ -1144,7 +1166,7 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po, auto unscaled_z = layer->slice_z; std::vector regions; //NOTE corresponding region ptr may be null, if the layer has zero perimeters - Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions); + Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions, po->config().perimeter_generator.value); for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) { process_perimeter_polygon(polygons[poly_index], unscaled_z, regions[poly_index], global_model_info, layer_seams); @@ -1622,6 +1644,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: void SeamPlacer::init(const Print &print, std::function throw_if_canceled_func) { using namespace SeamPlacerImpl; m_seam_per_object.clear(); + this->external_perimeters_first = print.default_region_config().external_perimeters_first; for (const PrintObject *po : print.objects()) { throw_if_canceled_func(); @@ -1690,7 +1713,6 @@ void SeamPlacer::init(const Print &print, std::function throw_if_can debug_export_points(m_seam_per_object[po].layers, po->bounding_box(), comparator); #endif } - this->external_perimeters_first = print.default_region_config().external_perimeters_first; } static constexpr float MINIMAL_POLYGON_SIDE = scaled(0.2f); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 68bdad07e45..94c39100a4f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -2281,10 +2281,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar assert(thickpaths[i - 1].last_point() == thickpaths[i].first_point()); } #endif - assert(thickpaths.front().first_point().x() == arachne_path.front().x()); - assert(thickpaths.front().first_point().y() == arachne_path.front().y()); - assert(thickpaths.back().last_point().x() == arachne_path.back().x()); - assert(thickpaths.back().last_point().y() == arachne_path.back().y()); + // thickpaths can be empty if extrusion_path is too short + assert(thickpaths.empty() || thickpaths.front().first_point().x() == arachne_path.front().x()); + assert(thickpaths.empty() || thickpaths.front().first_point().y() == arachne_path.front().y()); + assert(thickpaths.empty() || thickpaths.back().last_point().x() == arachne_path.back().x()); + assert(thickpaths.empty() || thickpaths.back().last_point().y() == arachne_path.back().y()); for (ExtrusionPath& path : thickpaths) { path.set_can_reverse(!is_loop); paths.push_back(std::move(path)); @@ -2303,10 +2304,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar assert(thickpaths[i - 1].last_point() == thickpaths[i].first_point()); } #endif - assert(thickpaths.front().first_point().x() == extrusion_path.front().x()); - assert(thickpaths.front().first_point().y() == extrusion_path.front().y()); - assert(thickpaths.back().last_point().x() == extrusion_path.back().x()); - assert(thickpaths.back().last_point().y() == extrusion_path.back().y()); + // thickpaths can be empty if extrusion_path is too short + assert(thickpaths.empty() || thickpaths.front().first_point().x() == extrusion_path.front().x()); + assert(thickpaths.empty() || thickpaths.front().first_point().y() == extrusion_path.front().y()); + assert(thickpaths.empty() || thickpaths.back().last_point().x() == extrusion_path.back().x()); + assert(thickpaths.empty() || thickpaths.back().last_point().y() == extrusion_path.back().y()); for (ExtrusionPath& path : thickpaths) { path.set_can_reverse(!is_loop); path.height = 0; @@ -2326,10 +2328,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar assert(thickpaths[i - 1].last_point() == thickpaths[i].first_point()); } #endif - assert(thickpaths.front().first_point().x() == extrusion_path.front().x()); - assert(thickpaths.front().first_point().y() == extrusion_path.front().y()); - assert(thickpaths.back().last_point().x() == extrusion_path.back().x()); - assert(thickpaths.back().last_point().y() == extrusion_path.back().y()); + // thickpaths can be empty if extrusion_path is too short + assert(thickpaths.empty() || thickpaths.front().first_point().x() == extrusion_path.front().x()); + assert(thickpaths.empty() || thickpaths.front().first_point().y() == extrusion_path.front().y()); + assert(thickpaths.empty() || thickpaths.back().last_point().x() == extrusion_path.back().x()); + assert(thickpaths.empty() || thickpaths.back().last_point().y() == extrusion_path.back().y()); for (ExtrusionPath& path : thickpaths) { path.set_can_reverse(!is_loop); path.height = no_small_flow ? 2 : 1; @@ -2349,10 +2352,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar assert(thickpaths[i - 1].last_point() == thickpaths[i].first_point()); } #endif - assert(thickpaths.front().first_point().x() == extrusion_path.front().x()); - assert(thickpaths.front().first_point().y() == extrusion_path.front().y()); - assert(thickpaths.back().last_point().x() == extrusion_path.back().x()); - assert(thickpaths.back().last_point().y() == extrusion_path.back().y()); + // thickpaths can be empty if extrusion_path is too short + assert(thickpaths.empty() || thickpaths.front().first_point().x() == extrusion_path.front().x()); + assert(thickpaths.empty() || thickpaths.front().first_point().y() == extrusion_path.front().y()); + assert(thickpaths.empty() || thickpaths.back().last_point().x() == extrusion_path.back().x()); + assert(thickpaths.empty() || thickpaths.back().last_point().y() == extrusion_path.back().y()); for (ExtrusionPath& path : thickpaths) { path.set_can_reverse(!is_loop); path.height = no_small_flow ? 3 : 2; @@ -2372,10 +2376,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar assert(thickpaths[i - 1].last_point() == thickpaths[i].first_point()); } #endif - assert(thickpaths.front().first_point().x() == extrusion_path.front().x()); - assert(thickpaths.front().first_point().y() == extrusion_path.front().y()); - assert(thickpaths.back().last_point().x() == extrusion_path.back().x()); - assert(thickpaths.back().last_point().y() == extrusion_path.back().y()); + // thickpaths can be empty if extrusion_path is too short + assert(thickpaths.empty() || thickpaths.front().first_point().x() == extrusion_path.front().x()); + assert(thickpaths.empty() || thickpaths.front().first_point().y() == extrusion_path.front().y()); + assert(thickpaths.empty() || thickpaths.back().last_point().x() == extrusion_path.back().x()); + assert(thickpaths.empty() || thickpaths.back().last_point().y() == extrusion_path.back().y()); for (ExtrusionPath& path : thickpaths) { // change flow to overhang one if too much. if (path.mm3_per_mm > this->overhang_flow.mm3_per_mm() ){ @@ -2420,11 +2425,41 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar } //FIXME from here, it's exactly the same as the other create_overhangs, please merge that into a function. - + + //(or not) + Point first_point(arachne_path.front().x(), arachne_path.front().y()); // reapply the nearest point search for starting point // We allow polyline reversal because Clipper may have randomly reversed polylines during clipping. if (!paths.empty()) - chain_and_reorder_extrusion_paths(paths, &paths.front().first_point()); + chain_and_reorder_extrusion_paths(paths, &first_point); + + //check if evvrything is okay (it can fail) + bool not_sorted_enough = false; + for (int i = 1; i < paths.size(); i++) { + if (!paths[i - 1].last_point().coincides_with_epsilon(paths[i].first_point())) { + not_sorted_enough = true; + break; + } + } + if (not_sorted_enough) { + Point other_point = paths[1].first_point(); + chain_and_reorder_extrusion_paths(paths, &other_point); + auto path = paths.back(); + paths.erase(paths.end()-1); + paths.insert(paths.begin(), path); + bool not_sorted_enough = false; + for (int i = 1; i < paths.size(); i++) { + if (paths[i - 1].last_point().coincides_with_epsilon(paths[i].first_point())) { + not_sorted_enough = true; + break; + } + } + if (not_sorted_enough) { + // do it manually by brute-force + // TODO + chain_and_reorder_extrusion_paths(paths, &first_point); + } + } for (int i = 1; i < paths.size(); i++) { // diff/inter can generate points with ~3-5 unit of diff. From f2c12ff373c01514e8c7e25ceb7e02347641eeec Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 15 Feb 2024 16:47:47 +0100 Subject: [PATCH 03/24] exemples of freq_params --- resources/ui_layout/example/freq_fff.ui | 6 +++ resources/ui_layout/example/print.as | 52 +++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/resources/ui_layout/example/freq_fff.ui b/resources/ui_layout/example/freq_fff.ui index a74cc590ca5..f937b56fdb2 100644 --- a/resources/ui_layout/example/freq_fff.ui +++ b/resources/ui_layout/example/freq_fff.ui @@ -13,3 +13,9 @@ group:freq_settings_event:no_title:no_search: setting:simple:script:bool:depends$brim_width:label$Brim:tooltip$Set the brim. Will be set to 5mm if nothing was previously set.:s_brim freq_purging_volumes end_line + line: + setting:simple:script:enum$hot$Hot$mild$Mild$cold$Cold:depends$bed_temperature$first_layer_bed_temperature:label$Bed temp:tooltip$Choose the bed you want.:full_width:s_bed_temp_fff + end_line + line: + setting:simple:script:enum$normal$Default$45$45°$custom$Custom:depends$init_z_rotate:label$Import angle:tooltip$The angle at which the parts are imported:full_width:s_orientation_fff + end_line diff --git a/resources/ui_layout/example/print.as b/resources/ui_layout/example/print.as index 775cac03e12..a4375b3bcbb 100644 --- a/resources/ui_layout/example/print.as +++ b/resources/ui_layout/example/print.as @@ -412,3 +412,55 @@ void s_noperi_set(string &out set_val, int idx) if (idx == 0) set_int("no_perimeter_unsupported_algo",0); else set_string("no_perimeter_unsupported_algo", "filled"); } + +// quick settings bed temp +int s_bed_temp_fff_get(string &out get_val) +{ + int bed_temperature = get_int("bed_temperature"); + int fl_bed_temperature = get_int("first_layer_bed_temperature"); + if (bed_temperature >= 70 && fl_bed_temperature >= 70) { + return 0; //hot + } + if (bed_temperature >= 45 && fl_bed_temperature >= 45) { + return 1; //mild + } + return 2; // cold +} +void s_bed_temp_fff_set(string &in new_val, int idx) +{ + if(idx == 0) { // hot + set_int("bed_temperature", 70); + set_int("first_layer_bed_temperature", 75); + } else if(idx == 1) { // mild + set_int("bed_temperature", 45); + set_int("first_layer_bed_temperature", 50); + } else if(idx == 2) { // cold + set_int("bed_temperature", 0); + set_int("first_layer_bed_temperature", 0); + } +} + + +// quick settings orientation +int s_orientation_fff_get(string &out get_val) +{ + float orientation = get_float("init_z_rotate"); + if (orientation == 0) { + return 0; // normal + } + if (orientation == 45) { + return 1; // 45° + } + return 3; // custom +} +void s_orientation_fff_set(string &in new_val, int idx) +{ + if(idx == 0) { // normal + set_float("init_z_rotate", 0); + } else if(idx == 1) { // 45° + set_float("init_z_rotate", 45); + } else if(idx == 2) { // reset + back_initial_value("init_z_rotate"); + } +} + From a3e0578ece87c0cf69bd218ef9be215b0beb2ecf Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 20 Feb 2024 13:38:05 +0100 Subject: [PATCH 04/24] log message for macos --- src/slic3r/GUI/BitmapCache.cpp | 1 + src/slic3r/GUI/ConfigWizard.cpp | 1 + src/slic3r/GUI/GLTexture.cpp | 1 + src/slic3r/GUI/GalleryDialog.cpp | 1 + 4 files changed, 4 insertions(+) diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 5f316ef6fee..2b3e1653b6e 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -242,6 +242,7 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, return it->second; wxImage image; + BOOST_LOG_TRIVIAL(error) << "Loading (forced png) image: '"<bool { if (wxFileExists(bitmap_file)) { + BOOST_LOG_TRIVIAL(error) << "Loading (png) image for wizard: '"< Date: Wed, 28 Feb 2024 19:36:27 +0100 Subject: [PATCH 05/24] debug log for loading png images (there may have some problem on macos?) --- src/slic3r/GUI/BitmapCache.cpp | 2 +- src/slic3r/GUI/ConfigWizard.cpp | 2 +- src/slic3r/GUI/GLTexture.cpp | 2 +- src/slic3r/GUI/GalleryDialog.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 2b3e1653b6e..16e5679459c 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -242,7 +242,7 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, return it->second; wxImage image; - BOOST_LOG_TRIVIAL(error) << "Loading (forced png) image: '"<bool { if (wxFileExists(bitmap_file)) { - BOOST_LOG_TRIVIAL(error) << "Loading (png) image for wizard: '"< Date: Wed, 21 Feb 2024 13:56:29 +0100 Subject: [PATCH 06/24] seam notch : allow almost convex/concave, so little imprecision doesn't prevent from detection. set seam_notch_angle min value to 180, as it doesn't supermerill/SuperSlicer#4143 --- src/libslic3r/GCode.cpp | 15 +++++++++++---- src/libslic3r/PrintConfig.cpp | 7 ++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 648a9791b71..996579b4627 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4111,9 +4111,11 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, bool is_convex = false; if (is_hole_loop) { //test if convex (as it's clockwise bc it's a hole, we have to do the opposite) - is_convex = polygon_to_test.convex_points().empty(); + // 3.07 instead of PI to allow for some convex outliers (sometimes, stl can be a bit imprecise) + is_convex = polygon_to_test.convex_points(3.07).empty(); } else { - is_convex = polygon_to_test.concave_points().empty(); + // 3.3 instead of PI to allow for some concave outliers (sometimes, stl can be a bit imprecise) + is_convex = polygon_to_test.concave_points(3.3).empty(); } if (is_convex) { // Computing circle center @@ -4196,11 +4198,16 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, //check if the current angle isn't too sharp double check_angle = 0; + // get min angle (and if at min or max value, push it a bit more to avoid not filtering outliers) + double min_angle = this->m_config.seam_notch_angle.value; + if(min_angle <= 179.9) min_angle -= 1; + if(min_angle >= 359.9) min_angle += 1; + min_angle *= PI / 180.; if (end_point.distance_to_square(start_point) < SCALED_EPSILON * SCALED_EPSILON) { check_angle = start_point.ccw_angle(prev_point, next_point); } else { check_angle = end_point.ccw_angle(prev_point, start_point); - if ((is_hole_loop ? -check_angle : check_angle) > this->m_config.seam_notch_angle.value * PI / 180.) { + if ((is_hole_loop ? -check_angle : check_angle) > min_angle) { BOOST_LOG_TRIVIAL(debug) << "notch abord: too big angle\n"; return; } @@ -4208,7 +4215,7 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, } assert(end_point != start_point); assert(end_point != next_point); - if ((is_hole_loop ? -check_angle : check_angle) > this->m_config.seam_notch_angle.value * PI / 180.) { + if ((is_hole_loop ? -check_angle : check_angle) > min_angle) { BOOST_LOG_TRIVIAL(debug) << "notch abord: too big angle\n"; return; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 10ef313a5f4..0d9ba14d4dc 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1889,7 +1889,7 @@ void PrintConfigDef::init_fff_params() "\nA value that only takes values as 'true' or 'false' will be a boolean)" "\nEvery other value will be parsed as a string as-is." "\nThese variables will be available as an array in the custom gcode (one item per extruder), don't forget to use them with the {current_extruder} index to get the current value." - " If a filament has a typo on the variable that change its type, then the parser will convert evrything to strings." + " If a filament has a typo on the variable that change its type, then the parser will convert everything to strings." "\nAdvice: before using a variable, it's safer to use the function 'default_XXX(variable_name, default_value)'" " (enclosed in bracket as it's a script) in case it's not set. You can replace XXX by 'int' 'bool' 'double' 'string'."); def->multiline = true; @@ -4708,9 +4708,10 @@ void PrintConfigDef::init_fff_params() def->label = L("max angle"); def->full_label = L("Seam notch maximum angle"); def->category = OptionCategory::perimeter; - def->tooltip = L("If the (external) angle at the seam is higher than this value, then no notch will be set. If the angle is too high, there isn't enough room for the notch."); + def->tooltip = L("If the (external) angle at the seam is higher than this value, then no notch will be set. If the angle is too high, there isn't enough room for the notch." + "\nCan't be lower than 180° or it filters everything. At 360, it allows everything."); def->sidetext = L("°"); - def->min = 0; + def->min = 180; def->max = 360; def->mode = comExpert | comSuSi; def->set_default_value(new ConfigOptionFloat(250)); From e5a406b224a24c41511c99b5d96b0ca62983a374 Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 22 Feb 2024 10:36:54 +0100 Subject: [PATCH 07/24] Add logs for when the slicer try to rename a file. supermerill/SuperSlicer#4135 --- src/libslic3r/utils.cpp | 43 ++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 2c89d1ff01c..71089ceea64 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -398,28 +398,38 @@ namespace WindowsSupport ScopedFileHandle from_handle; // Retry this a few times to defeat badly behaved file system scanners. for (unsigned retry = 0; retry != 200; ++ retry) { - if (retry != 0) - ::Sleep(10); + if (retry != 0) { + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: retry to open file"; + ::Sleep(10); + } from_handle = ::CreateFileW((LPWSTR)wide_from.data(), GENERIC_READ | DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (from_handle) break; - } + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: failed to open file '" << wide_from << "'"; + } if (! from_handle) return map_windows_error(GetLastError()); + BOOST_LOG_TRIVIAL(debug) << "Rename: Succeed to open source file '" << wide_from << "'"; // We normally expect this loop to succeed after a few iterations. If it // requires more than 200 tries, it's more likely that the failures are due to // a true error, so stop trying. for (unsigned retry = 0; retry != 200; ++ retry) { + if (retry != 0) { + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: retry to rename file"; + ::Sleep(10); + } auto errcode = rename_internal(from_handle, wide_to, true); if (errcode == std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category())) { + BOOST_LOG_TRIVIAL(debug) << "Rename: No support for SetFileInformationByHandle"; // Wine doesn't support SetFileInformationByHandle in rename_internal. // Fall back to MoveFileEx. if (std::error_code errcode2 = real_path_from_handle(from_handle, wide_from)) return errcode2; if (::MoveFileExW((LPCWSTR)wide_from.data(), (LPCWSTR)wide_to.data(), MOVEFILE_REPLACE_EXISTING)) return std::error_code(); + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: failed to move file from '" << wide_from << "' to '" << wide_to << "'"; return map_windows_error(GetLastError()); } @@ -438,20 +448,30 @@ namespace WindowsSupport // Another process might have raced with us and moved the existing file // out of the way before we had a chance to open it. If that happens, try // to rename the source file again. - if (errcode == std::errc::no_such_file_or_directory) - continue; + if (errcode == std::errc::no_such_file_or_directory) { + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: failed to create/open file '" << wide_to << "'"; + continue; + } return errcode; } + BOOST_LOG_TRIVIAL(debug) << "Rename: Succeed to create/open destination file '" << wide_to << "'"; BY_HANDLE_FILE_INFORMATION FI; - if (! ::GetFileInformationByHandle(to_handle, &FI)) - return map_windows_error(GetLastError()); + if (! ::GetFileInformationByHandle(to_handle, &FI)) { + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: failed to access file '" << wide_to << "' informations"; + return map_windows_error(GetLastError()); + } // Try to find a unique new name for the destination file. for (unsigned unique_id = 0; unique_id != 200; ++ unique_id) { + if (retry != 0) { + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: retry to create destination file"; + ::Sleep(10); + } std::wstring tmp_filename = wide_to + L".tmp" + std::to_wstring(unique_id); std::error_code errcode = rename_internal(to_handle, tmp_filename, false); if (errcode) { + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: error (" << errcode.value() << ") while renaming '" << wide_from << "' to '" << wide_to << "'"; if (errcode == std::make_error_code(std::errc::file_exists) || errcode == std::make_error_code(std::errc::permission_denied)) { // Again, another process might have raced with us and moved the file // before we could move it. Check whether this is the case, as it @@ -462,13 +482,17 @@ namespace WindowsSupport auto errcode = map_windows_error(GetLastError()); if (errcode == std::errc::no_such_file_or_directory) break; + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: access to '" << wide_to << "' is impossible (" << errcode.value() << ")"; return errcode; } BY_HANDLE_FILE_INFORMATION FI2; - if (! ::GetFileInformationByHandle(to_handle2, &FI2)) - return map_windows_error(GetLastError()); + if (! ::GetFileInformationByHandle(to_handle2, &FI2)) { + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: failed to access file '" << wide_to << "' informations"; + return map_windows_error(GetLastError()); + } if (FI.nFileIndexHigh != FI2.nFileIndexHigh || FI.nFileIndexLow != FI2.nFileIndexLow || FI.dwVolumeSerialNumber != FI2.dwVolumeSerialNumber) break; + // retry continue; } return errcode; @@ -482,6 +506,7 @@ namespace WindowsSupport // file, so we need to keep doing this until we succeed. } + BOOST_LOG_TRIVIAL(warning) << "Warn: Rename: failed to rename the file, abord operation."; // The most likely root cause. return std::make_error_code(std::errc::permission_denied); } From 002fa2cef49107416e0ec09673aa757103ef5841 Mon Sep 17 00:00:00 2001 From: supermerill Date: Fri, 23 Feb 2024 13:56:24 +0100 Subject: [PATCH 08/24] fix import project dialog & pattern substitution --- src/slic3r/GUI/GUI.cpp | 4 ++++ src/slic3r/GUI/Plater.cpp | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 7988625f1e7..4a82ac8d3ac 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -170,6 +170,10 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio bool is_infill = def->opt_key == "top_fill_pattern" || def->opt_key == "bottom_fill_pattern" || + def->opt_key == "solid_fill_pattern" || + def->opt_key == "bridge_fill_pattern" || + def->opt_key == "support_material_interface_pattern" || + def->opt_key == "brim_ears_pattern" || def->opt_key == "fill_pattern"; // Each infill doesn't use all list of infill declared in PrintConfig.hpp. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 12a0ca2fa0a..a167ada6911 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5519,7 +5519,7 @@ bool Plater::load_files(const wxArrayString& filenames) std::string filename = (*it).filename().string(); if (boost::algorithm::iends_with(filename, ".3mf") || boost::algorithm::iends_with(filename, ".amf")) { LoadType load_type = LoadType::Unknown; - if (!model().objects.empty()) { + if (!model().objects.empty() || wxGetApp().app_config->get("show_drop_project_dialog") == "1") { if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(it->string())) || (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) load_type = LoadType::LoadGeometry; From 477378a33ba9d7a9447eb5773463f9b6fd61a0c9 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 24 Feb 2024 16:27:53 +0100 Subject: [PATCH 09/24] allow multi-stl import also with single extruder printer supermerill/SuperSlicer#4149 --- src/slic3r/GUI/Plater.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a167ada6911..a277b98e610 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2416,7 +2416,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ auto *nozzle_dmrs = config->opt("nozzle_diameter"); - bool one_by_one = input_files.size() == 1 || printer_technology == ptSLA || nozzle_dmrs->values.size() <= 1; + bool one_by_one = input_files.size() == 1 || printer_technology == ptSLA; // || nozzle_dmrs->values.size() <= 1; // removed by bb (toa llow multi-import on a single extruder printer. if (! one_by_one) { for (const auto &path : input_files) { if (std::regex_match(path.string(), pattern_bundle)) { @@ -2729,10 +2729,11 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (new_model != nullptr && new_model->objects.size() > 1) { //wxMessageDialog msg_dlg(q, _L( - MessageDialog msg_dlg(q, _L( + MessageDialog msg_dlg(q, nozzle_dmrs->values.size() > 1 ? _L( "Multiple objects were loaded for a multi-material printer.\n" "Instead of considering them as multiple objects, should I consider\n" - "these files to represent a single object having multiple parts?") + "\n", + "these files to represent a single object having multiple parts?") + "\n": + _L("Load these files as a single object with multiple parts?\n"), _L("Multi-part object detected"), wxICON_WARNING | wxYES | wxNO); if (msg_dlg.ShowModal() == wxID_YES) { new_model->convert_multipart_object(nozzle_dmrs->values.size()); From d39f80ec0f5b3dd313eb6ceeb872cc208379304c Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 24 Feb 2024 17:30:36 +0100 Subject: [PATCH 10/24] fix ordering objects by min y supermerill/SuperSlicer#4147 --- src/libslic3r/GCode.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 996579b4627..eb26af4e94b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1248,16 +1248,18 @@ static inline std::vector sort_object_instances_by_max_y(c instances.emplace_back(&object->instances()[i]); // Calculate the convex hull of a printable object. Polygon poly = object->model_object()->convex_hull_2d( - Geometry::assemble_transform(Vec3d::Zero(), - object->instances()[i].model_instance->get_rotation(), - object->instances()[i].model_instance->get_scaling_factor(), - object->instances()[i].model_instance->get_mirror())); - poly.translate(object->instances()[i].shift - object->center_offset()); - coord_t min_y = poly.first_point().y(); - for (const Point& point : poly.points) - if (point.y() < min_y) - min_y = point.y(); - map_min_y[instances.back()] = min_y; + object->trafo() + // already in object->trafo() + //* Geometry::assemble_transform(Vec3d::Zero(), + // object->instances()[i].model_instance->get_rotation(), + // object->instances()[i].model_instance->get_scaling_factor(), + // object->instances()[i].model_instance->get_mirror()) + ); + BoundingBox bb(poly.points); + Vec2crd offset = object->instances()[i].shift - object->center_offset(); + bb.translate(offset.x(), offset.y()); + std::cout<<"bbobj (mod*inst) "<"<"< Date: Sun, 25 Feb 2024 17:53:32 +0100 Subject: [PATCH 11/24] fix medial axis ctrl-v old error --- src/libslic3r/Geometry/MedialAxis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Geometry/MedialAxis.cpp b/src/libslic3r/Geometry/MedialAxis.cpp index 62c3843eca8..f1d8788f780 100644 --- a/src/libslic3r/Geometry/MedialAxis.cpp +++ b/src/libslic3r/Geometry/MedialAxis.cpp @@ -1188,7 +1188,7 @@ get_coeff_from_angle_countour(Point& point, const ExPolygon& contour, coord_t mi Point point_before = id_near == 0 ? contour.contour.points.back() : contour.contour.points[id_near - 1]; Point point_after = id_near == contour.contour.points.size() - 1 ? contour.contour.points.front() : contour.contour.points[id_near + 1]; double angle2 = std::min(point_nearest.ccw_angle(point_before, point_after), point_nearest.ccw_angle(point_after, point_before)); - angle2 = abs(angle - PI / 2); + angle2 = abs(angle2 - PI / 2); angle = (angle + angle2) / 2; } From 9c9859f7131aee39ad58a960caf2eec5438e2250 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sun, 25 Feb 2024 17:54:11 +0100 Subject: [PATCH 12/24] profiles: change label_printed_objects to gcode_label_objects --- resources/profiles | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/profiles b/resources/profiles index 8f3bb94a9c6..ca25c7ec55d 160000 --- a/resources/profiles +++ b/resources/profiles @@ -1 +1 @@ -Subproject commit 8f3bb94a9c60be9a3b40d88a36ff05ba5cf2d6a8 +Subproject commit ca25c7ec55dcc6073da61e39692c321cdb6497dc From 192f568cbfb3c58e19cbe811de130ed80f41ab1a Mon Sep 17 00:00:00 2001 From: supermerill Date: Mon, 26 Feb 2024 12:26:15 +0100 Subject: [PATCH 13/24] Fix start gcode for complete_objects (sequential printing) with wipe tower Fix commit 3cd121 Note: now, with 3cd121, fan_mover post-process the start_gcode. It's needed to know the current fan speed, but it may be useful to add a kind of feature to "disable" the modification for this part. supermerill/SuperSlicer#4136 --- src/libslic3r/ExtrusionEntity.hpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index e417b0daa6b..154d36b58f7 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -462,12 +462,24 @@ class ExtrusionLoop : public ExtrusionEntity ExtrusionPaths paths; ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) , ExtrusionEntity(false) {} - ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role), ExtrusionEntity(false) { assert(this->first_point().coincides_with_epsilon(this->paths.back().polyline.back())); } - ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role), ExtrusionEntity(false) { assert(this->first_point().coincides_with_epsilon(this->paths.back().polyline.back())); } - ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role), ExtrusionEntity(false) - { this->paths.push_back(path); } - ExtrusionLoop(ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role), ExtrusionEntity(false) - { this->paths.emplace_back(std::move(path)); } + ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role), ExtrusionEntity(false) { + assert(!this->paths.empty()); + assert(this->first_point().coincides_with_epsilon(this->paths.back().polyline.back())); + } + ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role), ExtrusionEntity(false) { + assert(!this->paths.empty()); + assert(this->first_point().coincides_with_epsilon(this->paths.back().polyline.back())); + } + ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role), ExtrusionEntity(false) { + this->paths.push_back(path); + assert(!this->paths.empty()); + assert(this->first_point().coincides_with_epsilon(this->paths.back().polyline.back())); + } + ExtrusionLoop(ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role), ExtrusionEntity(false) { + this->paths.emplace_back(std::move(path)); + assert(!this->paths.empty()); + assert(this->first_point().coincides_with_epsilon(this->paths.back().polyline.back())); + } virtual bool is_loop() const override{ return true; } virtual ExtrusionEntity* clone() const override{ return new ExtrusionLoop (*this); } // Create a new object, initialize it with this object using the move semantics. From 9c7bde7e6316197b9737d7e130025729c8f1b907 Mon Sep 17 00:00:00 2001 From: supermerill Date: Mon, 26 Feb 2024 12:26:43 +0100 Subject: [PATCH 14/24] arachne can create (randomly) double-loops in one loop --- src/libslic3r/GCode.cpp | 20 ++++++++------------ src/libslic3r/PerimeterGenerator.cpp | 2 ++ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index eb26af4e94b..ddea273c04d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1777,18 +1777,14 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene m_last_pos_defined = false; - //flush FanMover buffer to avoid modifying the start gcode if it's manual. - if (this->config().start_gcode_manual && this->m_fan_mover.get() != nullptr) { - file.write(this->m_fan_mover.get()->process_gcode("", true)); - } - // Process filament-specific gcode. /* if (has_wipe_tower) { // Wipe tower will control the extruder switching, it will call the start_filament_gcode. } else { DynamicConfig config; config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); - file.writeln(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); + file.preamble_to_put_start_layer.append(this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); + preamble_to_put_start_layer.append("\n"); } */ @@ -1976,9 +1972,9 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene // Prusa Multi-Material wipe tower. if (has_wipe_tower && !layers_to_print.empty()) { m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get())); - file.write(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height")); + preamble_to_put_start_layer.append(m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height")); if (print.config().single_extruder_multi_material_priming) { - file.write(m_wipe_tower->prime(*this)); + preamble_to_put_start_layer.append(m_wipe_tower->prime(*this)); // Verify, whether the print overaps the priming extrusions. BoundingBoxf bbox_print(get_print_extrusions_extents(print)); coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON; @@ -1990,15 +1986,15 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene bool overlap = bbox_prime.overlap(bbox_print); if (print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware) { - file.write(this->retract()); - file.write("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz. + preamble_to_put_start_layer.append(this->retract()); + preamble_to_put_start_layer.append("M300 S800 P500\n"); // Beep for 500ms, tone 800Hz. if (overlap) { // Wait for the user to remove the priming extrusions. - file.write("M1 Remove priming towers and click button.\n"); + preamble_to_put_start_layer.append("M1 Remove priming towers and click button.\n"); } else { // Just wait for a bit to let the user check, that the priming succeeded. //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix. - file.write("M1 S10\n"); + preamble_to_put_start_layer.append("M1 S10\n"); } } else { // This is not Marlin, M1 command is probably not supported. diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 94c39100a4f..1c88e08799d 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -2953,6 +2953,8 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_extrusions(std::vector

is_closed) + assert((extrusion_path.front() - extrusion_path.back()).norm() < SCALED_EPSILON); paths = this->create_overhangs(extrusion_path, role, is_external); // Reapply the nearest point search for starting point. From 6c9d0dc1bd230b61fd1f08888e0e34045b5f0434 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 27 Feb 2024 10:52:04 +0100 Subject: [PATCH 15/24] Fix perimeter_bonding (was only working for 0% or 50%) updated disabled & tooltip --- src/libslic3r/PerimeterGenerator.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 2 +- src/slic3r/GUI/ConfigManipulation.cpp | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 1c88e08799d..9dd179f58c0 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -670,7 +670,7 @@ void PerimeterGenerator::process() && this->ext_perimeter_flow.spacing_ratio() == 1 && this->config->external_perimeters_first && this->object_config->perimeter_bonding.value > 0) { - this->infill_gap = (1 - this->object_config->perimeter_bonding.get_abs_value(1)) * ext_perimeter_spacing; + this->infill_gap = (this->object_config->perimeter_bonding.get_abs_value(1)) * ext_perimeter_spacing; this->ext_perimeter_spacing2 -= infill_gap; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0d9ba14d4dc..d916ea99d4f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4096,7 +4096,7 @@ void PrintConfigDef::init_fff_params() " You have to set perimeter_overlap and external_perimeter_overlap to 100%, or this setting has no effect." " 0: no effect, 50%: half of the nozzle will be over an already extruded perimeter while extruding a new one" ", unless it's an external one)." - "\nIt's very experimental, please report about the usefulness. It may be removed if there is no use for it."); + "\nNote: it needs the external and perimeter overlap to be at 100% and to print the external perimeter first."); def->sidetext = L("%"); def->min = 0; def->max = 50; diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 2ff5bfee7d5..21d11653f03 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -350,9 +350,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field(el, have_arachne); toggle_field("external_perimeters_vase", config->opt_bool("external_perimeters_first") && !config->opt_bool("perimeter_loop")); - for (auto el : { "external_perimeters_nothole", "external_perimeters_hole", "perimeter_bonding"}) + for (auto el : { "external_perimeters_nothole", "external_perimeters_hole"}) toggle_field(el, config->opt_bool("external_perimeters_first") && !have_arachne); + toggle_field("perimeter_bonding", config->opt_bool("external_perimeters_first") && !have_arachne && config->option("perimeter_overlap")->get_float() == 100.f && config->option("external_perimeter_overlap")->get_float() == 100.f); + for (auto el : {"perimeter_loop", "extra_perimeters_overhangs", "no_perimeter_unsupported_algo", "thin_perimeters", "overhangs_reverse", "perimeter_round_corners"}) toggle_field(el, have_perimeters && !have_arachne); From 273a9aaf11e6048e428307514dfe4176a7c0665d Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 27 Feb 2024 11:15:31 +0100 Subject: [PATCH 16/24] more responsive cancel on long prints. --- src/libslic3r/GCode.cpp | 107 +++++++++++++++++---------- src/libslic3r/GCode.hpp | 2 + src/libslic3r/Layer.cpp | 1 + src/libslic3r/LayerRegion.cpp | 1 + src/libslic3r/PerimeterGenerator.cpp | 15 +++- src/libslic3r/PerimeterGenerator.hpp | 1 + 6 files changed, 84 insertions(+), 43 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ddea273c04d..f0cfb77b1a6 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1131,7 +1131,9 @@ namespace DoExport { skirts.emplace_back(std::move(skirt_for_extruder)); } ooze_prevention.enable = true; + print.throw_if_canceled(); ooze_prevention.standby_points = offset(Slic3r::Geometry::convex_hull(skirts), float(scale_(3.))).front().equally_spaced_points(float(scale_(10.))); + print.throw_if_canceled(); #if 0 require "Slic3r/SVG.pm"; Slic3r::SVG::output( @@ -1312,6 +1314,8 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene const Print &print = print_mod; Print::StatusMonitor status_monitor{print_mod}; + this->m_throw_if_canceled = + [&print]() { print.throw_if_canceled(); }; //apply print config to m_config and m_writer, so we don't have to use print.config() instead // (and mostly to make m_writer.preamble() works) @@ -1323,7 +1327,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene //klipper can hide gcode into a macro, so add guessed init gcode to the processor. if (this->config().start_gcode_manual) { std::string gcode = m_writer.preamble(); - m_processor.process_string(gcode, [&print]() { print.throw_if_canceled(); }); + m_processor.process_string(gcode, this->m_throw_if_canceled); } if (! print.config().gcode_substitutions.values.empty()) { @@ -1373,7 +1377,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene std::sort(zs.begin(), zs.end()); m_layer_count = (uint32_t)(std::unique(zs.begin(), zs.end()) - zs.begin()); } - print.throw_if_canceled(); + this->m_throw_if_canceled(); //now that we have the layer count, init the status print.set_status(int(0), std::string(L("Generating G-code layer %s / %s")), std::vector{ std::to_string(0), std::to_string(layer_count()) }, PrintBase::SlicingStatus::DEFAULT | PrintBase::SlicingStatus::SECONDARY_STATE); @@ -1381,7 +1385,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene m_enable_cooling_markers = true; m_volumetric_speed = DoExport::autospeed_volumetric_limit(print); - print.throw_if_canceled(); + this->m_throw_if_canceled(); if (print.config().spiral_vase.value) m_spiral_vase = make_unique(print.config()); @@ -1405,7 +1409,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene thumbnails_format->value, true, [&file](const char *sz) { file.write(sz); }, - [&print]() { print.throw_if_canceled(); }); + this->m_throw_if_canceled); // Write information on the generator. file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); @@ -1423,7 +1427,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, thumbnails_tag_with_format ? thumbnails_tag_with_format->value : false, [&file](const char* sz) { file.write(sz); }, - [&print]() { print.throw_if_canceled(); }); + this->m_throw_if_canceled); } // Write notes (content of the Print Settings tab -> Notes) @@ -1439,7 +1443,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene if (! lines.empty()) file.write("\n"); } - print.throw_if_canceled(); + this->m_throw_if_canceled(); // Write some terse information on the slicing parameters. const PrintObject *first_object = print.objects().front(); @@ -1464,6 +1468,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene size_t nb_items = 0; std::wregex pattern(L"[^\\w]+", std::regex_constants::ECMAScript); for (PrintObject *print_object : print.objects()) { + this->m_throw_if_canceled(); this->m_ordered_objects.push_back(print_object); uint32_t copy_id = 0; for (const PrintInstance &print_instance : print_object->instances()) { @@ -1539,7 +1544,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene } file.write_format( "\n"); - print.throw_if_canceled(); + this->m_throw_if_canceled(); // adds tags for time estimators if (print.config().remaining_times.value) @@ -1629,7 +1634,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (uint16_t)-1); } - print.throw_if_canceled(); + this->m_throw_if_canceled(); m_cooling_buffer = make_unique(*this); m_cooling_buffer->set_current_extruder(initial_extruder_id); @@ -1793,18 +1798,17 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene preamble_to_put_start_layer.append(m_writer.set_fan(uint8_t(0), initial_extruder_id)); } - print.throw_if_canceled(); + this->m_throw_if_canceled(); // Set other general things. preamble_to_put_start_layer.append(this->preamble()); // Calculate wiping points if needed DoExport::init_ooze_prevention(print, m_ooze_prevention); - print.throw_if_canceled(); + this->m_throw_if_canceled(); // Collect custom seam data from all objects. - std::function throw_if_canceled_func = [&print]() { print.throw_if_canceled();}; - m_seam_placer.init(print, throw_if_canceled_func); + m_seam_placer.init(print, this->m_throw_if_canceled); //activate first extruder is multi-extruder and not in start-gcode if ((initial_extruder_id != (uint16_t)-1)) { @@ -1880,7 +1884,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene final_extruder_id = tool_ordering.last_extruder(); assert(final_extruder_id != (uint16_t)-1); } - print.throw_if_canceled(); + this->m_throw_if_canceled(); this->set_origin(unscale((*print_object_instance_sequential_active)->shift)); if (finished_objects > 0) { _move_to_print_object(preamble_to_put_start_layer, print, finished_objects, initial_extruder_id); @@ -1893,7 +1897,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene set_extra_lift(0, 0, print.config(), m_writer, initial_extruder_id); } //reinit the seam placer on the new object - m_seam_placer.init(print, throw_if_canceled_func); + m_seam_placer.init(print, this->m_throw_if_canceled); // Reset the cooling buffer internal state (the current position, feed rate, accelerations). m_cooling_buffer->reset(this->writer().get_position()); m_cooling_buffer->set_current_extruder(initial_extruder_id); @@ -1982,6 +1986,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz)); bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz)); BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print)); + this->m_throw_if_canceled(); bbox_prime.offset(0.5f); bool overlap = bbox_prime.overlap(bbox_print); @@ -2009,7 +2014,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene } } - print.throw_if_canceled(); + this->m_throw_if_canceled(); } // Process all layers of all objects (non-sequential mode) with a parallel pipeline: // Generate G-code, run the filters (vase mode, cooling buffer), run the G-code analyser @@ -2070,7 +2075,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene if (print.config().remaining_times.value) file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str()); - print.throw_if_canceled(); + this->m_throw_if_canceled(); // Get filament stats. file.write(DoExport::update_print_stats_and_format_filament_stats( @@ -2100,7 +2105,7 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene file.write(full_config); file.write("; " SLIC3R_APP_NAME "_config = end\n"); } - print.throw_if_canceled(); + this->m_throw_if_canceled(); //print thumbnails at the end instead of the start if requested (unless BTT / biqu thumbnail) if (thumbnails_end_file && thumbnails_end_file->value && (thumbnails_format == nullptr || thumbnails_format->value != GCodeThumbnailsFormat::BIQU)) { @@ -2115,9 +2120,9 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, thumbnails_tag_with_format ? thumbnails_tag_with_format->value: false, [&file](const char* sz) { file.write(sz); }, - [&print]() { print.throw_if_canceled(); }); + this->m_throw_if_canceled); } - print.throw_if_canceled(); + this->m_throw_if_canceled(); } void GCode::_move_to_print_object(std::string& gcode_out, const Print& print, size_t finished_objects, uint16_t initial_extruder_id) @@ -2201,7 +2206,7 @@ void GCode::process_layers( const LayerTools& layer_tools = tool_ordering.tools_for_layer(layer.first); if (m_wipe_tower && layer_tools.has_wipe_tower) m_wipe_tower->next_layer(); - print.throw_if_canceled(); + this->m_throw_if_canceled(); LayerResult result = this->process_layer(print, status_monitor, layer.second, layer_tools, &layer == &layers_to_print.back(), &print_object_instances_ordering, size_t(-1)); @@ -2211,40 +2216,43 @@ void GCode::process_layers( } }); const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&spiral_vase = *this->m_spiral_vase](LayerResult in) -> LayerResult { + [this, &spiral_vase = *this->m_spiral_vase](LayerResult in) -> LayerResult { if (in.nop_layer_result) return in; - + this->m_throw_if_canceled(); CNumericLocalesSetter locales_setter; spiral_vase.enable(in.spiral_vase_enable); return LayerResult{ spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; }); const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult { + [this, &pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult { + this->m_throw_if_canceled(); return pressure_equalizer.process_layer(std::move(in)); }); const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer](LayerResult in) -> std::string { + [this, &cooling_buffer = *this->m_cooling_buffer](LayerResult in) -> std::string { if (in.nop_layer_result) return in.gcode; - + this->m_throw_if_canceled(); CNumericLocalesSetter locales_setter; return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&self = *this->m_find_replace](std::string s) -> std::string { + [this, &self = *this->m_find_replace](std::string s) -> std::string { CNumericLocalesSetter locales_setter; + this->m_throw_if_canceled(); return self.process_layer(std::move(s)); }); const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&output_stream](std::string s) { + [this, &output_stream](std::string s) { CNumericLocalesSetter locales_setter; + this->m_throw_if_canceled(); output_stream.write(s); } ); const auto fan_mover = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string { + [this, &fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string { CNumericLocalesSetter locales_setter; if (config.fan_speedup_time.value != 0 || config.fan_kickstart.value > 0) { @@ -2257,6 +2265,7 @@ void GCode::process_layers( config.fan_speedup_overhangs.value, (float)config.fan_kickstart.value)); //flush as it's a whole layer + this->m_throw_if_canceled(); return fan_mover->process_gcode(in, true); } return in; @@ -2312,7 +2321,7 @@ void GCode::process_layers( } } else { LayerToPrint &layer = layers_to_print[layer_to_print_idx ++]; - print.throw_if_canceled(); + this->m_throw_if_canceled(); LayerResult result = this->process_layer(print, status_monitor, {std::move(layer)}, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, @@ -2323,34 +2332,39 @@ void GCode::process_layers( } }); const auto spiral_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&spiral_vase = *this->m_spiral_vase](LayerResult in)->LayerResult { + [this, &spiral_vase = *this->m_spiral_vase](LayerResult in)->LayerResult { if (in.nop_layer_result) return in; spiral_vase.enable(in.spiral_vase_enable); + this->m_throw_if_canceled(); return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; }); const auto pressure_equalizer = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult { + [this,&pressure_equalizer = *this->m_pressure_equalizer](LayerResult in) -> LayerResult { + this->m_throw_if_canceled(); return pressure_equalizer.process_layer(std::move(in)); }); const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer](LayerResult in)->std::string { + [this,&cooling_buffer = *this->m_cooling_buffer](LayerResult in)->std::string { if (in.nop_layer_result) return in.gcode; + this->m_throw_if_canceled(); return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); }); const auto find_replace = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&self = *this->m_find_replace](std::string s) -> std::string { + [this,&self = *this->m_find_replace](std::string s) -> std::string { + this->m_throw_if_canceled(); return self.process_layer(std::move(s)); }); const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&output_stream](std::string s) { + [this, &output_stream](std::string s) { + this->m_throw_if_canceled(); output_stream.write(s); } ); const auto fan_mover = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string { + [this, &fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string { if (config.fan_speedup_time.value != 0 || config.fan_kickstart.value > 0) { if (fan_mover.get() == nullptr) @@ -2361,6 +2375,7 @@ void GCode::process_layers( config.use_relative_e_distances.value, config.fan_speedup_overhangs.value, (float)config.fan_kickstart.value)); + this->m_throw_if_canceled(); //flush as it's a whole layer return fan_mover->process_gcode(in, true); } @@ -3472,10 +3487,13 @@ LayerResult GCode::process_layer( extruder_id, print_wipe_extrusions != 0) : island.by_region; - + gcode += this->extrude_infill(print, by_region_specific, true); + this->m_throw_if_canceled(); gcode += this->extrude_perimeters(print, by_region_specific); + this->m_throw_if_canceled(); gcode += this->extrude_infill(print, by_region_specific, false); + this->m_throw_if_canceled(); gcode += this->extrude_ironing(print, by_region_specific); //clear any leftover @@ -4589,12 +4607,14 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s if (nozzle_diam != 0 && setting_max_depth > nozzle_diam * 0.55) { // call travel_to to trigger retract, so we can check it (but don't use the travel) travel_to(gcode, pt, wipe_paths.front().role()); - if (m_writer.tool()->need_unretract()) + if (m_writer.tool()->need_unretract()) { + this->m_throw_if_canceled(); dist = coordf_t(check_wipe::max_depth(wipe_paths, scale_t(setting_max_depth), scale_t(nozzle_diam), [current_pos, current_point, vec_dist, vec_norm, angle](coord_t dist)->Point { Point pt = (current_pos + vec_dist * (2 * dist / vec_norm)).cast(); pt.rotate(angle, current_point); return pt; })); + } } // Shift by no more than a nozzle diameter. //FIXME Hiding the seams will not work nicely for very densely discretized contours! @@ -5990,6 +6010,7 @@ Polyline GCode::travel_to(std::string &gcode, const Point &point, ExtrusionRole // multi-hop travel path inside the configuration space if (will_cross_perimeter && this->needs_retraction(travel, role, scale_d(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0.4)) * 3) && can_avoid_cross_peri) { + this->m_throw_if_canceled(); travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled); } if(can_avoid_cross_peri) @@ -6035,7 +6056,8 @@ Polyline GCode::travel_to(std::string &gcode, const Point &point, ExtrusionRole m_avoid_crossing_perimeters.use_external_mp_once(); if (used_disabled_once) m_avoid_crossing_perimeters.disable_once(); - + + this->m_throw_if_canceled(); // Because of it, it is necessary to redo the thing travel = m_avoid_crossing_perimeters.travel_to(*this, point); updated_first_pos = true; @@ -6053,7 +6075,8 @@ Polyline GCode::travel_to(std::string &gcode, const Point &point, ExtrusionRole } //if needed, write the gcode_label_objects_end then gcode_label_objects_start _add_object_change_labels(gcode); - + + this->m_throw_if_canceled(); //if needed, remove points to avoid surcharging the printer. { const coordf_t scaled_min_length = scale_d(this->config().min_length.value); @@ -6199,16 +6222,20 @@ bool GCode::can_cross_perimeter(const Polyline& travel, bool offset) m_layer_slices_offseted.slices_offsetted = offset_ex(m_layer->lslices, -m_layer_slices_offseted.diameter * 1.5f); //remove top surfaces for (const LayerRegion* reg : m_layer->regions()) { + m_throw_if_canceled(); m_layer_slices_offseted.slices_offsetted = diff_ex(m_layer_slices_offseted.slices_offsetted, to_expolygons(reg->fill_surfaces.filter_by_type_flag(SurfaceType::stPosTop))); m_layer_slices_offseted.slices = diff_ex(m_layer_slices_offseted.slices, to_expolygons(reg->fill_surfaces.filter_by_type_flag(SurfaceType::stPosTop))); } } // test if a expoly contains the entire travel - for (const ExPolygon &poly : offset ? m_layer_slices_offseted.slices_offsetted : m_layer_slices_offseted.slices) + for (const ExPolygon &poly : + offset ? m_layer_slices_offseted.slices_offsetted : m_layer_slices_offseted.slices) { + m_throw_if_canceled(); if (poly.contains(travel)) { return false; } + } //} } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index b185e5942be..ee1527d0120 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -505,6 +505,8 @@ class GCode : ExtrusionVisitorConst { //some post-processing on the file, with their data class std::unique_ptr m_fan_mover; + std::function m_throw_if_canceled = [](){}; + std::string _extrude(const ExtrusionPath &path, const std::string &description, double speed = -1); void _extrude_line(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment); void _extrude_line_cut_corner(std::string& gcode_str, const Line& line, const double e_per_mm, const std::string& comment, Point& last_pos, const double path_width); diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 7cfc8d48086..7730827e9dc 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -255,6 +255,7 @@ void Layer::make_perimeters() // make perimeters SurfaceCollection fill_surfaces; + this->m_object->print()->throw_if_canceled(); layerm_config->make_perimeters(new_slices, &fill_surfaces); // assign fill_surfaces to each LayerRegion diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index be9f1dfca83..becd1881226 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -132,6 +132,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec g.overhang_flow = this->bridging_flow(frPerimeter); g.solid_infill_flow = this->flow(frSolidInfill); g.use_arachne = (this->layer()->object()->config().perimeter_generator.value == PerimeterGeneratorType::Arachne); + g.throw_if_canceled = [this]() { this->layer()->object()->print()->throw_if_canceled(); }; g.process(); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 9dd179f58c0..9110541b201 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -211,6 +211,7 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const // only_one_perimeter_top, from orca std::vector out_shell; if (loop_number > 0 && this->config->only_one_perimeter_top && !surface.has_mod_bridge() && upper_slices != nullptr) { + this->throw_if_canceled(); // Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces) ExPolygons non_top_polygons; ExPolygons fill_clip; @@ -287,6 +288,7 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const const coordf_t max_dist = bb.min.distance_to_square(bb.max); //detect astray points and delete them for (Arachne::VariableWidthLines &perimeter : perimeters) { + this->throw_if_canceled(); for (auto it_extrusion = perimeter.begin(); it_extrusion != perimeter.end();) { Point last_point = bb.min; for (auto it_junction = it_extrusion->junctions.begin(); it_junction != it_extrusion->junctions.end();) { @@ -385,6 +387,7 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const ordered_extrusions.reserve(all_extrusions.size()); while (ordered_extrusions.size() < all_extrusions.size()) { + this->throw_if_canceled(); size_t best_candidate = 0; double best_distance_sqr = std::numeric_limits::max(); bool is_best_closed = false; @@ -476,7 +479,8 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const } } } - + + this->throw_if_canceled(); if (ExtrusionEntityCollection extrusion_coll = _traverse_extrusions(ordered_extrusions); !extrusion_coll.empty()) { extrusion_coll.set_can_sort_reverse(false, false); this->loops->append(extrusion_coll); @@ -746,6 +750,7 @@ void PerimeterGenerator::process() } } } + this->throw_if_canceled(); // have to grown the perimeters if mill post-process MillingPostProcess miller(this->slices, this->lower_slices, config, object_config, print_config); @@ -788,6 +793,7 @@ void PerimeterGenerator::process() } else { surface_process_result = process_classic(loop_number, surface); } + this->throw_if_canceled(); // create one more offset to be used as boundary for fill @@ -851,7 +857,8 @@ void PerimeterGenerator::process() // intersect(growth(surface_process_result.inner_perimeter-gap) , surface_process_result.inner_perimeter), so you have the (surface_process_result.inner_perimeter - small gap) but without voids betweeng gap & surface_process_result.inner_perimeter infill_exp = intersection_ex(infill_exp, infill_exp_no_gap); } - + + this->throw_if_canceled(); //if any top_fills, grow them by ext_perimeter_spacing/2 to have the real un-anchored fill ExPolygons top_infill_exp = intersection_ex(surface_process_result.fill_clip, offset_ex(surface_process_result.top_fills, double(this->get_ext_perimeter_spacing() / 2))); if (!surface_process_result.top_fills.empty()) { @@ -1130,7 +1137,7 @@ ProcessSurfaceResult PerimeterGenerator::process_classic(int& loop_number, const } } - + this->throw_if_canceled(); // Add perimeters on overhangs : initialization ExPolygons overhangs_unsupported; if ((this->config->extra_perimeters_overhangs || (this->config->overhangs_reverse && this->layer->id() % 2 == 1)) @@ -1191,6 +1198,7 @@ ProcessSurfaceResult PerimeterGenerator::process_classic(int& loop_number, const ExPolygons no_last_gapfill; // we loop one time more than needed in order to find gaps after the last perimeter was applied for (int perimeter_idx = 0;; ++perimeter_idx) { // outer loop is 0 + this->throw_if_canceled(); // We can add more perimeters if there are uncovered overhangs // improvement for future: find a way to add perimeters only where it's needed. @@ -1696,6 +1704,7 @@ ProcessSurfaceResult PerimeterGenerator::process_classic(int& loop_number, const const double minarea = scale_d(scale_d(this->config->gap_fill_min_area.get_abs_value(unscaled((double)perimeter_width) * unscaled((double)perimeter_width)))); // check each gapfill area to see if it's printable. for (const ExPolygon& expoly : gaps_ex_to_test) { + this->throw_if_canceled(); //remove too small gaps that are too hard to fill. //ie one that are smaller than an extrusion with width of min and a length of max. if (expoly.area() > minarea) { diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 192d4691a91..7771b510a95 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -86,6 +86,7 @@ class PerimeterGenerator { const PrintObjectConfig *object_config; const PrintConfig *print_config; bool use_arachne = false; + std::function throw_if_canceled = []() {}; // Outputs: ExtrusionEntityCollection *loops; ExtrusionEntityCollection *gap_fill; From e3bbd95617296bc00b5517a146a800fc9eb1970d Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 28 Feb 2024 11:22:23 +0100 Subject: [PATCH 17/24] fix gap_fill_overlap supermerill/SuperSlicer#4138 --- src/libslic3r/PerimeterGenerator.cpp | 13 ++++--------- src/libslic3r/PerimeterGenerator.hpp | 2 -- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 9110541b201..2afbcc7ddc2 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -654,12 +654,6 @@ void PerimeterGenerator::process() // overhang perimeters this->m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm(); - //gap fill - this->gap_fill_spacing_external = this->config->gap_fill_overlap.get_abs_value(this->ext_perimeter_flow.with_spacing_ratio_from_width(1).scaled_spacing()) - + this->ext_perimeter_flow.scaled_width() * (1 - this->config->gap_fill_overlap.get_abs_value(1.)); - this->gap_fill_spacing = this->config->gap_fill_overlap.get_abs_value(this->perimeter_flow.with_spacing_ratio_from_width(1).scaled_spacing()) - + this->perimeter_flow.scaled_width() * (1 - this->config->gap_fill_overlap.get_abs_value(1.)); - // solid infill this->solid_infill_spacing = this->solid_infill_flow.scaled_spacing(); @@ -1442,11 +1436,11 @@ ProcessSurfaceResult PerimeterGenerator::process_classic(int& loop_number, const (round_peri ? min_round_spacing : 3)); if (perimeter_idx == 1) { append(gaps, diff_ex( - offset_ex(last, -0.5f * gap_fill_spacing_external), + offset_ex(last, -0.5f * this->get_ext_perimeter_spacing()), no_last_gapfill)); // safety offset } else { append(gaps, diff_ex( - offset_ex(last, -0.5f * gap_fill_spacing), + offset_ex(last, -0.5f * this->get_perimeter_spacing()), no_last_gapfill)); // safety offset } } @@ -1770,11 +1764,12 @@ ProcessSurfaceResult PerimeterGenerator::process_classic(int& loop_number, const md.build(polylines); } // create extrusion from lines + Flow gap_fill_flow = Flow::new_from_width(this->perimeter_flow.width(), this->perimeter_flow.nozzle_diameter(),this->perimeter_flow.height(), this->config->gap_fill_overlap.get_abs_value(1.), false); if (!polylines.empty()) { this->gap_fill->append(Geometry::thin_variable_width( polylines, erGapFill, - this->solid_infill_flow, + gap_fill_flow, scale_t(this->print_config->resolution_internal), true)); /* Make sure we don't infill narrow parts that are already gap-filled diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 7771b510a95..b0d36ca9a7b 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -146,8 +146,6 @@ class PerimeterGenerator { coord_t ext_perimeter_width; coord_t get_ext_perimeter_width() { return ext_perimeter_width; } coord_t ext_perimeter_spacing; coord_t get_ext_perimeter_spacing() { return ext_perimeter_spacing; } coord_t ext_perimeter_spacing2; coord_t get_ext_perimeter_spacing2() { return ext_perimeter_spacing2; } - coord_t gap_fill_spacing; coord_t get_gap_fill_spacing() { return gap_fill_spacing; } - coord_t gap_fill_spacing_external; coord_t get_gap_fill_spacing_external() { return gap_fill_spacing_external; } coord_t infill_gap; coord_t get_infill_gap() { return infill_gap; } coord_t solid_infill_spacing; coord_t get_solid_infill_spacing() { return solid_infill_spacing; } bool round_peri; From a8cfaf2e8a5e17b605f7e47d58c7b408290944e3 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 28 Feb 2024 16:45:22 +0100 Subject: [PATCH 18/24] fix bridge_type = layer_height supermerill/SuperSlicer#4137 --- src/libslic3r/Layer.hpp | 8 ++++---- src/libslic3r/PrintObject.cpp | 14 ++++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 532d49a1fdc..7d95b72898f 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -124,10 +124,10 @@ class Layer Layer *upper_layer; Layer *lower_layer; bool slicing_errors; - coordf_t slice_z; // Z used for slicing in unscaled coordinates - coordf_t print_z; // Z used for printing in unscaled coordinates - coordf_t height; // layer height in unscaled coordinates - coordf_t bottom_z() const { return this->print_z - this->height; } + double slice_z; // Z used for slicing in unscaled coordinates + double print_z; // Z used for printing in unscaled coordinates + double height; // layer height in unscaled coordinates + double bottom_z() const { return this->print_z - this->height; } // Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry // (with possibly differing extruder ID and slicing parameters) and merged. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 32a9decb644..034614dffd6 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2304,9 +2304,11 @@ bool PrintObject::invalidate_state_by_config_options( if (layer_it == m_layers.begin()) continue; - Layer *layer = *layer_it; - LayerRegion *layerm = layer->m_regions[region_id]; - Flow bridge_flow = layerm->bridging_flow(frSolidInfill); + Layer *layer = *layer_it; + LayerRegion *layerm = layer->m_regions[region_id]; + const Flow bridge_flow = layerm->bridging_flow(frSolidInfill); + const float bridge_height = std::max(float(layer->height + EPSILON), bridge_flow.height()); + const coord_t bridge_width = bridge_flow.scaled_width(); // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; @@ -2320,7 +2322,7 @@ bool PrintObject::invalidate_state_by_config_options( Polygons to_bridge_pp = internal_solid; // iterate through lower layers spanned by bridge_flow - double bottom_z = layer->print_z - bridge_flow.height() - EPSILON; + double bottom_z = layer->print_z - (bridge_flow.height()+0.1) - EPSILON; //TODO take into account sparse ratio! double protrude_by = bridge_flow.height - layer->height; for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) { const Layer* lower_layer = m_layers[i]; @@ -2347,7 +2349,7 @@ bool PrintObject::invalidate_state_by_config_options( { to_bridge.clear(); //choose between two offsets the one that split the less the surface. - float min_width = float(bridge_flow.scaled_width()) * 3.f; + float min_width = float(bridge_width) * 3.f; // opening : offset2-+ ExPolygons to_bridgeOK = opening_ex(to_bridge_pp, min_width, min_width); for (ExPolygon& expolys_to_check_for_thin : union_ex(to_bridge_pp)) { @@ -2356,7 +2358,7 @@ bool PrintObject::invalidate_state_by_config_options( ExPolygons not_bridge = diff_ex(ExPolygons{ expolys_to_check_for_thin }, collapsed); int try1_count = bridge.size() + not_bridge.size(); if (try1_count > 1) { - min_width = float(bridge_flow.scaled_width()) * 1.5f; + min_width = float(bridge_width) * 1.5f; collapsed = offset2_ex(ExPolygons{ expolys_to_check_for_thin }, -min_width, +min_width * 1.5f); ExPolygons bridge2 = intersection_ex(collapsed, ExPolygons{ expolys_to_check_for_thin }); not_bridge = diff_ex(ExPolygons{ expolys_to_check_for_thin }, collapsed); From caa4a0173266f68adf0eff8cca343950f91ad646 Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 28 Feb 2024 18:28:22 +0100 Subject: [PATCH 19/24] new setting: fill_aligned_z to align the spacing of sparse infill (default to true) supermerill/SuperSlicer#4131 --- resources/ui_layout/default/print.ui | 1 + src/libslic3r/Fill/Fill.cpp | 25 ++++++++++--- src/libslic3r/Fill/Fill3DHoneycomb.cpp | 2 +- src/libslic3r/Fill/FillBase.cpp | 8 ++-- src/libslic3r/Fill/FillBase.hpp | 16 +++++--- src/libslic3r/Fill/FillConcentric.cpp | 6 +-- src/libslic3r/Fill/FillGyroid.cpp | 22 ++++------- src/libslic3r/Fill/FillGyroid.hpp | 5 +-- src/libslic3r/Fill/FillHoneycomb.cpp | 7 +++- src/libslic3r/Fill/FillRectilinear.cpp | 21 ++++++----- src/libslic3r/Fill/FillRectilinear.hpp | 2 +- src/libslic3r/Preset.cpp | 1 + src/libslic3r/Print.hpp | 6 +++ src/libslic3r/PrintConfig.cpp | 12 ++++++ src/libslic3r/PrintConfig.hpp | 1 + src/libslic3r/PrintObject.cpp | 51 ++++++++++++++++++++++---- src/slic3r/GUI/ConfigManipulation.cpp | 2 +- 17 files changed, 132 insertions(+), 56 deletions(-) diff --git a/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui index 959d028473a..70c4aa3bbc8 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -169,6 +169,7 @@ group:title_width$0:Infill setting:tags$Advanced$Expert$Prusa:label_left:label_width$6:label$Sparse:width$8:sidetext_width$1:fill_density setting:tags$Advanced$Expert$Prusa:label_width$0:label$_:fill_pattern setting:label$_:width$18:infill_connection + setting:label$Aligned:fill_aligned_z end_line line:_ setting:label$Connection length:label_width$25:sidetext_width$7:width$12:infill_anchor_max diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 08fd8e60c5e..913a30cb45c 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -62,7 +62,6 @@ struct SurfaceFillParams : FillParams RETURN_COMPARE_NON_EQUAL(anchor_length); RETURN_COMPARE_NON_EQUAL(fill_exactly); - RETURN_COMPARE_NON_EQUAL(flow.width()); RETURN_COMPARE_NON_EQUAL(flow.height()); RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter()); RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge()); @@ -80,7 +79,10 @@ struct SurfaceFillParams : FillParams RETURN_COMPARE_NON_EQUAL(config->bridge_speed_internal); RETURN_COMPARE_NON_EQUAL(config->gap_fill_speed); RETURN_COMPARE_NON_EQUAL(config->print_extrusion_multiplier); + RETURN_COMPARE_NON_EQUAL(max_sparse_infill_spacing); } + if (config == nullptr || rhs.config == nullptr || max_sparse_infill_spacing == 0) + RETURN_COMPARE_NON_EQUAL(flow.width()); assert(*this == rhs); return false; } @@ -116,6 +118,7 @@ struct SurfaceFillParams : FillParams this->fill_exactly == rhs.fill_exactly && this->flow == rhs.flow && this->role == rhs.role && + this->max_sparse_infill_spacing == rhs.max_sparse_infill_spacing && this->priority == rhs.priority; } }; @@ -282,17 +285,18 @@ std::vector group_fills(const Layer &layer) //FIXME FLOW decide what to use // Internal infill. Calculating infill line spacing independent of the current layer height and 1st layer status, // so that internall infill will be aligned over all layers of the current region. - //params.spacing = layerm.region().flow(*layer.object(), frInfill, layer.object()->config().layer_height, false).spacing(); + //params.spacing = layerm.region().flow(*layer.object(), frInfill, layer.heigh, false).spacing(); // it's internal infill, so we can calculate a generic flow spacing // for all layers, for avoiding the ugly effect of // misaligned infill on first layer because of different extrusion width and // layer height - params.spacing = layerm.region().flow( + Flow infill_flow = layerm.region().flow( *layer.object(), frInfill, - layer.object()->config().layer_height.value, // TODO: handle infill_every_layers? + layer.height, // TODO: handle infill_every_layers? layer.id() - ).spacing(); + ); + params.spacing = infill_flow.spacing(); // Anchor a sparse infill to inner perimeters with the following anchor length: params.anchor_length = float(region_config.infill_anchor); @@ -302,6 +306,16 @@ std::vector group_fills(const Layer &layer) if (region_config.infill_anchor_max.percent) params.anchor_length_max = float(params.anchor_length_max * 0.01 * params.spacing); params.anchor_length = std::min(params.anchor_length, params.anchor_length_max); + + //sparse infill, compute the max width if needed + if (region_config.fill_aligned_z) { + //don't use fill_aligned_z if the pattern can't use it. + if (params.pattern != ipHilbertCurve && params.pattern != ipArchimedeanChords && + params.pattern != ipOctagramSpiral && params.pattern != ipScatteredRectilinear && + params.pattern != ipLightning) { + params.max_sparse_infill_spacing = unscaled(layer.object()->get_sparse_max_spacing()); + } + } } auto it_params = set_surface_params.find(params); @@ -805,6 +819,7 @@ void Layer::make_ironing() }; std::vector by_extruder; + // not using layer.height? double default_layer_height = this->object()->config().layer_height; for (LayerRegion *layerm : m_regions) diff --git a/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 1a124e5a65d..eb852658b76 100644 --- a/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -143,7 +143,7 @@ void Fill3DHoneycomb::_fill_surface_single( { // no rotation is supported for this infill pattern BoundingBox bb = expolygon.contour.bounding_box(); - coord_t distance = coord_t(scale_(this->get_spacing()) / params.density); + coord_t distance = _line_spacing_for_density(params); // align bounding box to a multiple of our honeycomb grid module // (a module is 2*$distance since one $distance half-module is diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 1ef1482db3a..4bb29ebb644 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -310,9 +310,11 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ -coord_t Fill::_line_spacing_for_density(float density) const +coord_t Fill::_line_spacing_for_density(const FillParams& params) const { - return scale_t(this->get_spacing() / density); + if(params.max_sparse_infill_spacing > 0) + return scale_t(params.max_sparse_infill_spacing / params.density); + return scale_t(this->get_spacing() / params.density); } //FIXME: add recent improvmeent from perimetergenerator: avoid thick gapfill @@ -3174,7 +3176,7 @@ void Fill::connect_base_support(Polylines &&infill_ordered, const std::vector 0) ? params.max_sparse_infill_spacing : spacing) / params.density; const double min_arch_length = 1.3 * line_spacing; const double trim_length = line_half_width * 0.3; diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index e7aa5dd4c77..c6a876de601 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -46,7 +46,7 @@ struct FillParams // Fill density, fraction in <0, 1> float density { 0.f }; - // bridge offset from the centerline. + // bridge offset from the centerline. (scaled) coord_t bridge_offset = -1; // Fill extruding flow multiplier, fraction in <0, 1>. Used by "over bridge compensation" @@ -94,8 +94,12 @@ struct FillParams // For Concentric infill, to switch between Classic and Arachne. bool use_arachne { false }; - // Layer height for Concentric infill with Arachne. - coordf_t layer_height { 0.f }; + + // Layer height for Concentric infill with Arachne. (unscaled) + float layer_height { 0.f }; + + // sparse infill width to use to create the pattern (0 if not used) (unscaled) + float max_sparse_infill_spacing { 0.f }; }; static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor)."); @@ -129,7 +133,7 @@ class Fill #endif protected: // in unscaled coordinates, please use init (after settings all others settings) as some algos want to modify the value - coordf_t spacing_priv; + double spacing_priv; public: virtual ~Fill() {} @@ -140,7 +144,7 @@ class Fill void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; } virtual void init_spacing(coordf_t spacing, const FillParams ¶ms) { this->spacing_priv = spacing; } - coordf_t get_spacing() const { return spacing_priv; } + double get_spacing() const { return spacing_priv; } // Do not sort the fill lines to optimize the print head path? virtual bool no_sort() const { return false; } @@ -191,7 +195,7 @@ class Fill virtual float _layer_angle(size_t idx) const { return can_angle_cross && (idx & 1) ? float(M_PI/2.) : 0; } - virtual coord_t _line_spacing_for_density(float density) const; + virtual coord_t _line_spacing_for_density(const FillParams& params) const; virtual std::pair _infill_direction(const Surface *surface) const; diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 3afd1c357ec..ad5208c183a 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -15,7 +15,7 @@ FillConcentric::init_spacing(coordf_t spacing, const FillParams ¶ms) { Fill::init_spacing(spacing, params); if (params.density > 0.9999f && !params.dont_adjust) { - this->spacing_priv = unscaled(this->_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params.density))); + this->spacing_priv = unscaled(this->_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params))); } } @@ -30,7 +30,7 @@ FillConcentric::_fill_surface_single( // no rotation is supported for this infill pattern BoundingBox bounding_box = expolygon.contour.bounding_box(); - coord_t distance = _line_spacing_for_density(params.density); + coord_t distance = _line_spacing_for_density(params); if (params.density > 0.9999f && !params.dont_adjust) { //it's == Slic3r::FillConcentric::_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params.density)) because of the init_spacing() distance = scale_t(this->get_spacing()); @@ -115,7 +115,7 @@ FillConcentricWGapFill::fill_surface_extrusion( // no rotation is supported for this infill pattern BoundingBox bounding_box = expolygon.contour.bounding_box(); - coord_t distance = _line_spacing_for_density(params.density); + coord_t distance = _line_spacing_for_density(params); if (params.density > 0.9999f && !params.dont_adjust) { distance = scale_t(this->get_spacing()); } diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index f97eaaea3b2..c276b76b28d 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -139,9 +139,6 @@ static Polylines make_gyroid_waves(coordf_t gridZ, coordf_t scaleFactor, double return result; } -// FIXME: needed to fix build on Mac on buildserver -constexpr double FillGyroid::PatternTolerance; - void FillGyroid::_fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, @@ -154,27 +151,24 @@ void FillGyroid::_fill_surface_single( expolygon.rotate(-infill_angle); BoundingBox bb = expolygon.contour.bounding_box(); - // Density adjusted to have a good %of weight. - double density_adjusted = std::max(0., params.density * DensityAdjust); // Distance between the gyroid waves in scaled coordinates. - coord_t distance = coord_t(scale_(this->get_spacing()) / density_adjusted); + coord_t line_spacing = _line_spacing_for_density(params); + // Density adjusted to have a good %of weight. + line_spacing /= FillGyroid::DENSITY_ADJUST; // align bounding box to a multiple of our grid module - bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance))); + bb.merge(align_to_grid(bb.min, Point(2*M_PI*line_spacing, 2*M_PI*line_spacing))); // tolerance in scaled units. clamp the maximum tolerance as there's // no processing-speed benefit to do so beyond a certain point - const coordf_t scaleFactor = scale_d(this->get_spacing()) / density_adjusted; - const double tolerance_old = std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) / unscaled(scaleFactor); - const double tolerance_old2 = std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) * density_adjusted / this->get_spacing(); - const double tolerance = params.config->get_computed_value("resolution_internal") * density_adjusted / this->get_spacing(); + const double tolerance = params.config->get_computed_value("resolution_internal") / unscaled(line_spacing); // generate pattern Polylines polylines = make_gyroid_waves( scale_d(this->z), - scaleFactor, - ceil(bb.size()(0) / distance) + 1., - ceil(bb.size()(1) / distance) + 1., + coordf_t(line_spacing), + ceil(bb.size()(0) / line_spacing) + 1., + ceil(bb.size()(1) / line_spacing) + 1., tolerance); // shift the polyline to the grid origin diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp index b235f1778c5..c5e0dbf9184 100644 --- a/src/libslic3r/Fill/FillGyroid.hpp +++ b/src/libslic3r/Fill/FillGyroid.hpp @@ -15,10 +15,7 @@ class FillGyroid : public Fill Fill* clone() const override { return new FillGyroid(*this); } // Density adjustment to have a good %of weight. - static constexpr double DensityAdjust = 2.44; - - // Gyroid upper resolution tolerance (mm^-2) - static constexpr double PatternTolerance = 0.2; + static constexpr double DENSITY_ADJUST = 2.44; protected: diff --git a/src/libslic3r/Fill/FillHoneycomb.cpp b/src/libslic3r/Fill/FillHoneycomb.cpp index 93f50570161..41dd02d62c2 100644 --- a/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/src/libslic3r/Fill/FillHoneycomb.cpp @@ -15,13 +15,16 @@ void FillHoneycomb::_fill_surface_single( ExPolygon expolygon, Polylines &polylines_out) const { + double my_spacing = this->get_spacing(); + if(params.max_sparse_infill_spacing > 0) + my_spacing = params.max_sparse_infill_spacing; // cache hexagons math - CacheID cache_id(params.density, this->get_spacing()); + CacheID cache_id(params.density, my_spacing); Cache::iterator it_m = FillHoneycomb::cache.find(cache_id); if (it_m == FillHoneycomb::cache.end()) { it_m = FillHoneycomb::cache.insert(it_m, std::pair(cache_id, CacheData())); CacheData &m = it_m->second; - coord_t min_spacing = coord_t(scale_(this->get_spacing())); + coord_t min_spacing = scale_t(my_spacing); m.distance = coord_t(double(min_spacing) / params.density); m.hex_side = coord_t(double(m.distance) / (sqrt(3)/2)); m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3); diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 6bc1ecae6d7..3c571e79e93 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -761,7 +761,7 @@ FillRectilinear::init_spacing(coordf_t spacing, const FillParams& params) //remove this code path becaus it's only really useful for squares at 45° and it override a setting // define flow spacing according to requested density //if (params.full_infill() && !params.dont_adjust) { - // this->spacing = unscaled(this->_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params.density))); + // this->spacing = unscaled(this->_adjust_solid_spacing(bounding_box.size()(0), _line_spacing_for_density(params))); //} } @@ -2932,7 +2932,7 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa rotate_vector.first += angleBase; assert(params.density > 0.0001f); - coord_t line_spacing = _line_spacing_for_density(params.density); + coord_t line_spacing = _line_spacing_for_density(params); // On the polygons of poly_with_offset, the infill lines will be connected. ExPolygonWithOffset poly_with_offset( @@ -2984,6 +2984,9 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa // Intersect a set of equally spaced vertical lines with expolygon. std::vector segs = _vert_lines_for_polygon(poly_with_offset, bounding_box, params, line_spacing); + if (segs.empty()) + return false; + slice_region_by_vertical_lines(this, segs, poly_with_offset); //all the works is done HERE @@ -3138,7 +3141,7 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar Polylines fill_lines; coord_t line_width = scale_t(this->get_spacing()); - coord_t line_spacing = scale_t(this->get_spacing() / params.density); + coord_t line_spacing = _line_spacing_for_density(params); std::pair rotate_vector = this->_infill_direction(surface); for (const SweepParams& sweep : sweep_params) { // Rotate polygons so that we can work with vertical lines here @@ -3209,7 +3212,7 @@ Polylines FillStars::fill_surface(const Surface *surface, const FillParams ¶ Polylines polylines_out; if (!this->fill_surface_by_multilines( surface, params, - { { 0.f, 0.f }, { float(M_PI / 3.), 0.f }, { float(2. * M_PI / 3.), float((3. / 2.) * this->get_spacing() / params.density) } }, + { { 0.f, 0.f }, { float(M_PI / 3.), 0.f }, { float(2. * M_PI / 3.), float((3. / 2.) * unscaled(_line_spacing_for_density(params))) } }, polylines_out)) BOOST_LOG_TRIVIAL(error) << "FillStars::fill_surface() failed to fill a region."; return polylines_out; @@ -3236,7 +3239,7 @@ Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams ExPolygonWithOffset poly_with_offset(surface->expolygon, - rotate_vector.first, scale_t(this->overlap - 0.5 * this->get_spacing())); if (poly_with_offset.n_contours > 0) { Polylines fill_lines; - coord_t line_spacing = scale_t(this->get_spacing() / params.density); + coord_t line_spacing = _line_spacing_for_density(params); // Create infill lines, keep them vertical. make_fill_lines(poly_with_offset, rotate_vector.second.rotated(- rotate_vector.first), 0, 0, line_spacing, 0, fill_lines, params); // Both the poly_with_offset and polylines_out are rotated, so the infill lines are strictly vertical. @@ -3271,14 +3274,14 @@ float FillScatteredRectilinear::_layer_angle(size_t idx) const return randomFloatFromSeed((uint32_t) idx) * (float) M_PI; } -coord_t FillScatteredRectilinear::_line_spacing_for_density(float density) const +coord_t FillScatteredRectilinear::_line_spacing_for_density(const FillParams& params) const { /* The density argument is ignored, we first generate lines at 100% density, then prune some generated lines * later to achieve the target density */ - (void) density; - - return coord_t(scale_(this->get_spacing()) / 1.0); + if(params.max_sparse_infill_spacing > 0) + return scale_t(params.max_sparse_infill_spacing); + return scale_t(this->get_spacing()); } Polylines FillScatteredRectilinear::fill_surface(const Surface *surface, const FillParams ¶ms) const diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp index dae36dc6ca7..27f9288ab53 100644 --- a/src/libslic3r/Fill/FillRectilinear.hpp +++ b/src/libslic3r/Fill/FillRectilinear.hpp @@ -129,7 +129,7 @@ class FillScatteredRectilinear : public FillRectilinear protected: float _layer_angle(size_t idx) const override; std::vector _vert_lines_for_polygon(const ExPolygonWithOffset& poly_with_offset, const BoundingBox& bounding_box, const FillParams& params, coord_t line_spacing) const override; - coord_t _line_spacing_for_density(float density) const override; + coord_t _line_spacing_for_density(const FillParams& params) const override; }; class FillRectilinearSawtooth : public FillRectilinear { diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index b7bb188cd5b..032b76b92b0 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -507,6 +507,7 @@ static std::vector s_Preset_print_options { "ironing_speed", "ironing_spacing", "ironing_angle", + "fill_aligned_z", "fill_angle", "fill_angle_cross", "fill_angle_increment", diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 6cba4c389a7..96a3804ff5e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -301,6 +301,8 @@ class PrintObject : public PrintObjectBaseWithState prepare_adaptive_infill_data(); FillLightning::GeneratorPtr prepare_lightning_infill_data(); @@ -434,6 +437,9 @@ class PrintObject : public PrintObjectBaseWithStateset_default_value(new ConfigOptionString(L("(Unknown)"))); def->cli = ConfigOptionDef::nocli; + def = this->add("fill_aligned_z", coBool); + def->label = L("Align sparse infill in z"); + def->category = OptionCategory::infill; + def->tooltip = L("The voids of the pattern of sparse infill grows with the extrusion width, to keep the same percentage of fill." + " With this setting set to true, the algorithms will now only use the highest sparse infill width avaialble to create the pattern." + " This way, the pattern can still be aligned even if the width is changing (from first layer width, from a modifier, from aanother extruder with different diameter)." + "\nExperimental: works only for infill that won't depends on the fill area. So for infill where it's not useful (Hilbert, Archimedean, Octagram, Scattered, Lightning), this setting is disabled." + "\n This setting is useful for rectilinear, monotonic, grid, trianlge, star, cubic, gyroid, honeycomb, 3D honeycomb, adaptative cubic, support cubic patterns."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("fill_angle", coFloat); def->label = L("Fill"); def->full_label = L("Fill angle"); @@ -8093,6 +8104,7 @@ std::unordered_set prusa_export_to_remove_keys = { "filament_use_fast_skinnydip", "filament_use_skinnydip", "filament_wipe_advanced_pigment", +"fill_aligned_z", "fill_angle_increment", "fill_smooth_distribution", "fill_smooth_width", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 5fe902cc900..6b491ae338a 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -830,6 +830,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, only_one_perimeter_first_layer)) ((ConfigOptionBool, only_one_perimeter_top)) ((ConfigOptionBool, only_one_perimeter_top_other_algo)) + ((ConfigOptionBool, fill_aligned_z)) ((ConfigOptionFloat, fill_angle)) ((ConfigOptionBool, fill_angle_cross)) ((ConfigOptionFloat, fill_angle_increment)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 034614dffd6..ab143542a8b 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -107,14 +107,14 @@ namespace Slic3r { return status; } -std::vector> PrintObject::all_regions() const + std::vector> PrintObject::all_regions() const { - std::vector> out; - out.reserve(m_shared_regions->all_regions.size()); - for (const std::unique_ptr ®ion : m_shared_regions->all_regions) - out.emplace_back(*region.get()); - return out; - } + std::vector> out; + out.reserve(m_shared_regions->all_regions.size()); + for (const std::unique_ptr ®ion : m_shared_regions->all_regions) + out.emplace_back(*region.get()); + return out; + } @@ -541,9 +541,45 @@ std::vector> PrintObject::all_regions( } // for each layer #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + //compute m_max_sparse_spacing for fill_aligned_z + _compute_max_sparse_spacing(); + this->set_done(posPrepareInfill); } + void PrintObject::_compute_max_sparse_spacing() + { + m_max_sparse_spacing = 0; + std::atomic_int64_t max_sparse_spacing; + //tbb::parallel_for( + // tbb::blocked_range(0, m_layers.size()), + // [this, &max_sparse_spacing](const tbb::blocked_range& range) { + //for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++layer_idx) { + m_print->throw_if_canceled(); + const Layer *layer = m_layers[layer_idx]; + for (const LayerRegion *layerm : layer->regions()) { + // check if region has sparse infill. + for (const Surface &surface : layerm->fill_surfaces.surfaces) { + if (surface.has_fill_sparse()) { + coord_t spacing = layerm->region().flow(*this, frInfill, layer->height, layer->id()).scaled_spacing(); + // update atomic to max + int64_t prev_value = max_sparse_spacing.load(); + std::cout<<"will update "< "<option("fill_density")->value > 0; // infill_extruder uses the same logic as in Print::extruders() - for (auto el : { "fill_pattern", "infill_connection", "infill_every_layers", "infill_only_where_needed", + for (auto el : { "fill_aligned_z", "fill_pattern", "infill_connection", "infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder", "infill_anchor_max" }) toggle_field(el, have_infill); // Only allow configuration of open anchors if the anchoring is enabled. From 3bdabd0d8086d5f372e5dfd50e5596d22f88290d Mon Sep 17 00:00:00 2001 From: supermerill Date: Wed, 28 Feb 2024 20:32:18 +0100 Subject: [PATCH 20/24] update to gcode viewer legend: . switch min & max position for gcode viewer if vertical . fix outliers . add preference for max decimals (now 2 by default instead of 3) . allow outliers switch for all numeric fields . hide log scale if discrete mode --- src/libslic3r/AppConfig.cpp | 3 ++ src/slic3r/GUI/GCodeViewer.cpp | 98 ++++++++++++++++++++++++++-------- src/slic3r/GUI/GCodeViewer.hpp | 23 ++++---- src/slic3r/GUI/Preferences.cpp | 15 +++++- 4 files changed, 106 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 4850b9a91b1..da87e70e831 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -302,6 +302,9 @@ void AppConfig::set_defaults() if (get("font_size").empty()) set("font_size", "0"); + if (get("gcodeviewer_decimals").empty()) + set("gcodeviewer_decimals", "2"); + //get default color from the ini file //try to load colors from ui file diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index a87ff2c4d01..095719a33a8 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -261,6 +261,7 @@ void GCodeViewer::Extrusions::Range::update_from(const float f_value) // note: not needed to clear caches, as update isn't called after chache creation and there is always a reset before } + bool GCodeViewer::Extrusions::Range::has_outliers() const { bool has_outliers = false; @@ -281,6 +282,26 @@ bool GCodeViewer::Extrusions::Range::has_outliers() const return has_outliers; } +bool GCodeViewer::Extrusions::Range::can_have_outliers(float ratio) const +{ + bool has_outliers = false; + size_t min_count = ratio * total_count / 20; + for (size_t idx = 0; idx < 19; ++idx) { + if (counts[idx] > 0) { + has_outliers = counts[idx] <= min_count; + break; + } + } + if (!has_outliers) + for (size_t idx = 19; idx > 0; --idx) { + if (counts[idx] > 0) { + has_outliers = counts[idx] <= min_count; + break; + } + } + return has_outliers; +} + void GCodeViewer::Extrusions::Range::reset() { @@ -620,12 +641,34 @@ GCodeViewer::Extrusions::Range* GCodeViewer::Extrusions::Ranges::get(EViewType t } -GCodeViewer::Extrusions::Ranges::Ranges(){ +GCodeViewer::Extrusions::Ranges::Ranges(uint8_t max_decimals) : + // Color mapping by layer height. + height(max_decimals, false), + // Color mapping by extrusion width. + width(max_decimals), + // Color mapping by feedrate. + feedrate(std::min(max_decimals, uint8_t(1))), + // Color mapping by fan speed. + fan_speed(0), + // Color mapping by volumetric extrusion rate. + volumetric_rate(max_decimals), + // Color mapping by volumetric extrusion mm3/mm. + volumetric_flow(max_decimals), + // Color mapping by extrusion temperature. + temperature(0), + // Color mapping by layer time. + layer_duration(0, true), + // Color mapping by time. + elapsed_time(0,true) { for (size_t i = 0; i < size_t(EViewType::Count); i++) { this->min_max_cstr_id[i] = std::pair{std::string("##min") + std::to_string(i), std::string("##max") + std::to_string(i)}; } } + + +GCodeViewer::Extrusions::Extrusions() : ranges(std::max(0, std::min(6, atoi(Slic3r::GUI::get_app_config()->get("gcodeviewer_decimals").c_str())))) {} + GCodeViewer::SequentialRangeCap::~SequentialRangeCap() { if (ibo > 0) glsafe(::glDeleteBuffers(1, &ibo)); @@ -3521,8 +3564,8 @@ void GCodeViewer::render_legend(float& legend_height) const PrintEstimatedStatistics::Mode& time_mode = m_print_statistics.modes[static_cast(m_time_estimate_mode)]; bool show_estimated_time = time_mode.time > 0.0f && (m_view_type == EViewType::FeatureType || (m_view_type == EViewType::ColorPrint && !time_mode.custom_gcode_times.empty())); - bool show_switch_show_outliers = (m_view_type == EViewType::VolumetricFlow || m_view_type == EViewType::VolumetricRate) && range && range->has_outliers(); - bool show_switch_log_scale = (m_view_type == EViewType::LayerTime); + bool show_switch_show_outliers = range && range->can_have_outliers(0.01f); + bool show_switch_log_scale = (m_view_type == EViewType::LayerTime) && (!range || !range->is_discrete_mode()); bool show_min_max_field = range && (range->get_user_min() || range->get_user_max() || range->count_discrete() > 5); bool show_min_max_field_same_line = (m_view_type == EViewType::VolumetricFlow || m_view_type == EViewType::VolumetricRate || m_view_type == EViewType::LayerTime || m_view_type == EViewType::Chronology); bool show_switch_discrete = range && range->count_discrete() > 2 && range->count_discrete() <= Range_Colors_Details.size(); @@ -4356,29 +4399,40 @@ void GCodeViewer::render_legend(float& legend_height) } std::string min_label = _u8L("min"); std::string max_label = _u8L("max"); - float max_label_size = std::max(imgui.calc_text_size(min_label).x, imgui.calc_text_size(min_label).y); - max_label_size += imgui.scaled(1.f); - // draw text - ImGui::AlignTextToFramePadding(); - imgui.text(min_label); - if (show_min_max_field_same_line) + + if (show_min_max_field_same_line) { + // draw min + imgui.text(min_label); ImGui::SameLine(); - else - ImGui::SameLine(max_label_size); - ImGui::PushItemWidth(imgui.get_style_scaling() * size); - ImGui::InputFloat(m_extrusions.ranges.min_max_cstr_id[size_t(m_view_type)].first.c_str(), &mod_min, 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); - if (show_min_max_field_same_line) + ImGui::PushItemWidth(imgui.get_style_scaling() * size); + ImGui::InputFloat(m_extrusions.ranges.min_max_cstr_id[size_t(m_view_type)].second.c_str(), &mod_max, 0.0f, + 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); + // draw max ImGui::SameLine(); - else - ImGui::AlignTextToFramePadding(); - imgui.text(max_label); - if (show_min_max_field_same_line) + imgui.text(max_label); ImGui::SameLine(); - else - ImGui::SameLine(max_label_size); - ImGui::PushItemWidth(imgui.get_style_scaling() * size); - ImGui::InputFloat(m_extrusions.ranges.min_max_cstr_id[size_t(m_view_type)].second.c_str(), &mod_max, 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); + ImGui::PushItemWidth(imgui.get_style_scaling() * size); + ImGui::InputFloat(m_extrusions.ranges.min_max_cstr_id[size_t(m_view_type)].first.c_str(), &mod_min, 0.0f, + 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); + } else { + float label_size = std::max(imgui.calc_text_size(min_label).x, imgui.calc_text_size(min_label).y); + label_size += imgui.scaled(1.f); + // draw max + imgui.text(max_label); + ImGui::SameLine(label_size); + ImGui::PushItemWidth(imgui.get_style_scaling() * size); + ImGui::InputFloat(m_extrusions.ranges.min_max_cstr_id[size_t(m_view_type)].second.c_str(), &mod_max, 0.0f, + 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); + // draw min + ImGui::AlignTextToFramePadding(); + imgui.text(min_label); + ImGui::SameLine(label_size); + ImGui::PushItemWidth(imgui.get_style_scaling() * size); + ImGui::InputFloat(m_extrusions.ranges.min_max_cstr_id[size_t(m_view_type)].first.c_str(), &mod_min, 0.0f, + 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal, 0.f); + } + // refresh? if (mod_min != old_min) if(range->set_user_min(mod_min)) need_refresh_render_paths = true; diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 7e6c44e7504..a5c4c035ea3 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -264,6 +264,7 @@ class GCodeViewer float get_absolute_min() const { return m_full_precision_min; } bool is_log_scale() const { return m_log_scale; } bool set_log_scale(bool log_scale); + bool can_have_outliers(float ratio) const; bool has_outliers() const; float get_ratio_outliers() const { return m_ratio_outlier; } bool set_ratio_outliers(float ratio); @@ -277,29 +278,29 @@ class GCodeViewer struct Ranges { // Color mapping by layer height. - Range height{3, false}; + Range height; // Color mapping by extrusion width. - Range width{3}; + Range width; // Color mapping by feedrate. - Range feedrate{1}; + Range feedrate; // Color mapping by fan speed. - Range fan_speed{0}; + Range fan_speed; // Color mapping by volumetric extrusion rate. - Range volumetric_rate{3}; + Range volumetric_rate; // Color mapping by volumetric extrusion mm3/mm. - Range volumetric_flow{3}; + Range volumetric_flow; // Color mapping by extrusion temperature. - Range temperature{0}; + Range temperature; // Color mapping by layer time. - Range layer_duration{0, true}; + Range layer_duration; // Color mapping by time. - Range elapsed_time{0,true}; + Range elapsed_time; Range* get(EViewType type); std::pair min_max_cstr_id[size_t(EViewType::Count)]; - Ranges(); + Ranges(uint8_t max_decimals); void reset() { height.reset(); @@ -317,6 +318,8 @@ class GCodeViewer unsigned int role_visibility_flags{ 0 }; Ranges ranges; + Extrusions(); + void reset_role_visibility_flags() { role_visibility_flags = 0; for (unsigned int i = 0; i < erCount; ++i) { diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index f0bc224a466..c3715877fff 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -668,7 +668,20 @@ void PreferencesDialog::build(size_t selected_tab) option = Option(def, "show_layer_time_doubleslider"); m_optgroups_gui.back()->append_single_option_line(option); } - + + + def.label = L("Decimals for gcode viewer colors"); + def.type = coInt; + def.tooltip = L("On the gcode viewer window, how many decimals are used to separate colors?" + " It's used for height, width, volumetric rate, section. Default is 2."); + def.set_default_value(new ConfigOptionInt{ atoi(app_config->get("gcodeviewer_decimals").c_str()) }); + option = Option(def, "gcodeviewer_decimals"); + option.opt.min = 0; + option.opt.max = 5; + option.opt.width = 6; + m_optgroups_gui.back()->append_single_option_line(option); + // as it's quite hard to detect a change and then clean & reload the gcode data... then asking for relaod is easier. + m_values_need_restart.push_back("gcodeviewer_decimals"); activate_options_tab(m_optgroups_gui.back(), 3); From a5d257d991153c07b822127c42c125d77ac30860 Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 29 Feb 2024 10:25:20 +0100 Subject: [PATCH 21/24] Updates to prusa config export Make sure each setting is ignored of marked as prusa-compatible. --- src/libslic3r/PrintConfig.cpp | 71 ++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 784d0812f4c..385c5d89841 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1025,6 +1025,7 @@ void PrintConfigDef::init_fff_params() def = this->add("colorprint_heights", coFloats); def->label = L("Colorprint height"); def->category = OptionCategory::slicing; + def->mode = comExpert | comPrusa; // note: hidden setting def->tooltip = L("Heights at which a filament change is to occur. "); def->set_default_value(new ConfigOptionFloats { }); @@ -1063,9 +1064,11 @@ void PrintConfigDef::init_fff_params() def = this->add("compatible_printers_condition_cummulative", coStrings); def->set_default_value(new ConfigOptionStrings()); def->cli = ConfigOptionDef::nocli; + def->mode = comNone | comPrusa; // note: hidden setting def = this->add("compatible_prints_condition_cummulative", coStrings); def->set_default_value(new ConfigOptionStrings()); def->cli = ConfigOptionDef::nocli; + def->mode = comNone | comPrusa; // note: hidden setting def = this->add("complete_objects", coBool); def->label = L("Complete individual objects"); @@ -1221,6 +1224,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Default filament profile"); def->tooltip = L("Default filament profile associated with the current printer profile. " "On selection of the current printer profile, this filament profile will be activated."); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionStrings()); def->cli = ConfigOptionDef::nocli; @@ -1242,6 +1246,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Default print profile"); def->tooltip = L("Default print profile associated with the current printer profile. " "On selection of the current printer profile, this print profile will be activated."); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString()); def->cli = ConfigOptionDef::nocli; @@ -1675,6 +1680,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::extruders; def->tooltip = L("The extruder to use (unless more specific extruder settings are specified). " "This value overrides perimeter and infill extruders, but not the support extruders."); + def->mode = comAdvancedE | comPrusa; // note: hidden setting def->min = 0; // 0 = inherit defaults def->enum_labels.push_back(L("default")); // override label for item 0 def->enum_labels.push_back("1"); @@ -1779,6 +1785,7 @@ void PrintConfigDef::init_fff_params() def->category = OptionCategory::extruders; def->tooltip = L("Use this option to set the axis letter associated with your printer's extruder " "(usually E but some printers use A)."); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString("E")); def = this->add("extrusion_multiplier", coFloats); @@ -2270,10 +2277,12 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionFloats { 0. }); def = this->add("filament_settings_id", coStrings); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionStrings { "" }); def->cli = ConfigOptionDef::nocli; def = this->add("filament_vendor", coString); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString(L("(Unknown)"))); def->cli = ConfigOptionDef::nocli; @@ -2451,7 +2460,7 @@ void PrintConfigDef::init_fff_params() def->tooltip = L("The first layer will be grown / shrunk in the XY plane by the configured value " "to compensate for the 1st layer squish aka an Elephant Foot effect. (should be negative = inwards)"); def->sidetext = L("mm"); - def->mode = comAdvancedE | comSuSi; + def->mode = comAdvancedE | comSuSi | comPrusa; // just a rename & inverted of prusa 's elefant_foot def->set_default_value(new ConfigOptionFloat(0)); def = this->add("first_layer_size_compensation_layers", coInt); @@ -4305,12 +4314,13 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("%"); def->min = 0; def->max = 100; - def->mode = comSimpleAE | comSuSi; + def->mode = comAdvancedE | comSuSi; def->set_default_value(new ConfigOptionInt(0)); def = this->add("printer_model", coString); def->label = L("Printer type"); def->tooltip = L("Type of the printer."); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString()); def->cli = ConfigOptionDef::nocli; @@ -4327,24 +4337,29 @@ void PrintConfigDef::init_fff_params() def = this->add("printer_vendor", coString); def->label = L("Printer vendor"); def->tooltip = L("Name of the printer vendor."); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString()); def->cli = ConfigOptionDef::nocli; def = this->add("printer_variant", coString); def->label = L("Printer variant"); def->tooltip = L("Name of the printer variant. For example, the printer variants may be differentiated by a nozzle diameter."); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString()); def->cli = ConfigOptionDef::nocli; def = this->add("print_settings_id", coString); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString("")); def->cli = ConfigOptionDef::nocli; def = this->add("printer_settings_id", coString); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString("")); def->cli = ConfigOptionDef::nocli; def = this->add("physical_printer_settings_id", coString); + def->mode = comNone | comPrusa; // note: hidden setting def->set_default_value(new ConfigOptionString("")); def->cli = ConfigOptionDef::nocli; @@ -5791,6 +5806,7 @@ void PrintConfigDef::init_fff_params() "is slightly above the number of available cores/processors."); def->readonly = true; def->min = 1; + def->mode = comExpert | comPrusa; // note: hidden setting (and should be a preference) { int threads = (unsigned int)boost::thread::hardware_concurrency(); def->set_default_value(new ConfigOptionInt(threads > 0 ? threads : 2)); @@ -8064,7 +8080,6 @@ std::unordered_set prusa_export_to_remove_keys = { "curve_smoothing_angle_convex", "curve_smoothing_cutoff_dist", "curve_smoothing_precision", -//"default_fan_speed", used to convert to min_fan_speed & fan_always_on "default_speed", "enforce_full_fill_volume", "exact_last_layer_height", @@ -8072,6 +8087,7 @@ std::unordered_set prusa_export_to_remove_keys = { "external_perimeter_acceleration", "external_perimeter_cut_corners", "external_perimeter_extrusion_spacing", +"external_perimeter_extrusion_change_odd_layers", "external_perimeter_fan_speed", "external_perimeter_overlap", "external_perimeters_hole", @@ -8089,6 +8105,7 @@ std::unordered_set prusa_export_to_remove_keys = { "fan_speedup_time", "feature_gcode", "filament_cooling_zone_pause", +"filament_custom_variables", "filament_dip_extraction_speed", "filament_dip_insertion_speed", "filament_enable_toolchange_part_fan", @@ -8106,6 +8123,8 @@ std::unordered_set prusa_export_to_remove_keys = { "filament_wipe_advanced_pigment", "fill_aligned_z", "fill_angle_increment", +"fill_angle_cross", +"fill_angle_template", "fill_smooth_distribution", "fill_smooth_width", "fill_top_flow_ratio", @@ -8128,20 +8147,29 @@ std::unordered_set prusa_export_to_remove_keys = { "gap_fill_min_width", "gap_fill_overlap", "gcode_filename_illegal_char", +"gcode_precision_e", +"gcode_precision_xyz", "hole_size_compensation", "hole_size_threshold", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "hole_to_polyhole", "infill_connection", +"infill_connection_bottom", "infill_connection_bridge", +"infill_connection_solid", +"infill_connection_top", "infill_dense_algo", "infill_dense", +"infill_extrusion_change_odd_layers", "infill_extrusion_spacing", "infill_fan_speed", +"init_z_rotate", "ironing_acceleration", +"ironing_angle", "lift_min", "machine_max_acceleration_travel", +"max_gcode_per_second", "max_speed_reduction", "milling_after_z", "milling_cutter", @@ -8166,10 +8194,11 @@ std::unordered_set prusa_export_to_remove_keys = { "overhangs_fan_speed", "overhangs_reverse_threshold", "overhangs_reverse", -"overhangs_speed", "overhangs_speed_enforce", "overhangs_width_speed", +"parallel_objects_step", "perimeter_bonding", +"perimeter_extrusion_change_odd_layers", "perimeter_extrusion_spacing", "perimeter_fan_speed", "perimeter_loop_seam", @@ -8177,20 +8206,26 @@ std::unordered_set prusa_export_to_remove_keys = { "perimeter_overlap", "perimeter_round_corners", "print_extrusion_multiplier", +"print_custom_variables", "print_retract_length", "print_retract_lift", "print_temperature", +"printer_custom_variables", "printhost_client_cert", "printhost_client_cert_password", "raft_layer_height", "raft_interface_layer_height", "remaining_times_type", +"resolution_internal", "retract_lift_first_layer", "retract_lift_top", "retract_lift_before_travel", "seam_angle_cost", "seam_gap", "seam_gap_external", +"solid_over_perimeters", +"filament_seam_gap", // filament override +"filament_seam_gap_external", // filament override "seam_notch_all", "seam_notch_angle", "seam_notch_inner", @@ -8203,6 +8238,7 @@ std::unordered_set prusa_export_to_remove_keys = { "small_perimeter_max_length", "small_perimeter_min_length", "solid_fill_pattern", +"solid_infill_extrusion_change_odd_layers", "solid_infill_acceleration", "solid_infill_extrusion_spacing", "solid_infill_fan_speed", @@ -8253,10 +8289,26 @@ std::unordered_set prusa_export_to_remove_keys = { "wipe_inside_start", "wipe_only_crossing", "wipe_speed", +"filament_wipe_extra_perimeter", // filament override +"filament_wipe_inside_depth", // filament override +"filament_wipe_inside_end", // filament override +"filament_wipe_inside_start", // filament override +"filament_wipe_only_crossing", // filament override +"filament_wipe_speed", // filament override +"wipe_tower_speed", +"wipe_tower_wipe_starting_speed", +"xy_size_compensation", "xy_inner_size_compensation", "z_step", }; +std::unordered_set prusa_export_to_change_keys = +{ +"default_fan_speed", // used to convert to min_fan_speed & fan_always_on +"overhangs_width", +"overhangs_speed", +}; + std::map PrintConfigDef::to_prusa(t_config_option_key& opt_key, std::string& value, const DynamicConfig& all_conf) { std::map new_entries; @@ -8264,7 +8316,13 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& if (prusa_export_to_remove_keys.find(opt_key) != prusa_export_to_remove_keys.end()) { opt_key = ""; value = ""; - } else if (opt_key.find("_pattern") != std::string::npos) { + return new_entries; + } + if (!opt_key.empty() && prusa_export_to_change_keys.find(opt_key) == prusa_export_to_change_keys.end()) { + auto mode = all_conf.def()->get(opt_key)->mode; + assert( (mode & comPrusa) == comPrusa); + } + if (opt_key.find("_pattern") != std::string::npos) { if ("smooth" == value || "smoothtriple" == value || "smoothhilbert" == value || "rectiwithperimeter" == value || "scatteredrectilinear" == value || "rectilineargapfill" == value || "sawtooth" == value) { value = "rectilinear"; } else if ("concentricgapfill" == value) { @@ -8435,6 +8493,7 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& } // --end-- custom gcode: --end-- + return new_entries; } @@ -9246,7 +9305,7 @@ std::string validate(const FullPrintConfig& cfg) if (cfg.extra_perimeters || cfg.extra_perimeters_overhangs || cfg.extra_perimeters_odd_layers) return "Can't make more than one perimeter when spiral vase mode is enabled"; if (cfg.overhangs_reverse) - return "Can't reverse the direction of the perimeter every layer when spiral vase mode is enabled"; + return "Can't reverse the direction of the overhangs every layer when spiral vase mode is enabled"; } // extrusion widths From 326e889e19653a1dcb3287ef658340c723c78647 Mon Sep 17 00:00:00 2001 From: supermerill Date: Thu, 29 Feb 2024 13:38:04 +0100 Subject: [PATCH 22/24] Setting for alternating perimeter printing direction every even layer supermerill/SuperSlicer#4159 --- resources/ui_layout/default/print.ui | 1 + src/libslic3r/ExtrusionEntity.hpp | 5 +++++ src/libslic3r/Layer.cpp | 1 + src/libslic3r/PerimeterGenerator.cpp | 13 +++++++++---- src/libslic3r/Preset.cpp | 1 + src/libslic3r/PrintConfig.cpp | 18 +++++++++++++++--- src/libslic3r/PrintConfig.hpp | 1 + src/libslic3r/PrintObject.cpp | 1 + src/slic3r/GUI/ConfigManipulation.cpp | 8 +++++--- 9 files changed, 39 insertions(+), 10 deletions(-) diff --git a/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui index 70c4aa3bbc8..92d3863a64c 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -66,6 +66,7 @@ group:label_width$12:Overhangs line:Extrusion direction setting:sidetext_width$2:overhangs_reverse setting:label_width$12:width$5:overhangs_reverse_threshold + setting:label$All perimeters:sidetext_width$2:perimeter_reverse end_line group:sidetext_width$5:Advanced setting:width$25:no_perimeter_unsupported_algo diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 154d36b58f7..2e750307716 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -629,6 +629,11 @@ struct HasInfillVisitor : public HasRoleVisitor{ struct HasSolidInfillVisitor : public HasRoleVisitor{ void default_use(const ExtrusionEntity &entity) override { found = is_solid_infill(entity.role()); }; }; +struct HasThisRoleVisitor : public HasRoleVisitor{ + ExtrusionRole role_to_find; + HasThisRoleVisitor(ExtrusionRole role) : role_to_find(role) {} + void default_use(const ExtrusionEntity &entity) override { found = entity.role() == role_to_find; }; +}; //call simplify for all paths. diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 7730827e9dc..5e5eaec9b49 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -204,6 +204,7 @@ void Layer::make_perimeters() && config.perimeter_loop == other_config.perimeter_loop && config.perimeter_loop_seam == other_config.perimeter_loop_seam && config.perimeter_overlap == other_config.perimeter_overlap + && config.perimeter_reverse == other_config.perimeter_reverse && config.perimeter_speed == other_config.perimeter_speed // it os mandatory? can't this be set at gcode.cpp? && config.print_extrusion_multiplier == other_config.print_extrusion_multiplier && config.small_perimeter_speed == other_config.small_perimeter_speed diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 2afbcc7ddc2..18e9c1ea03f 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -56,7 +56,6 @@ namespace Slic3r { // } //}; - PerimeterGeneratorLoops get_all_Childs(PerimeterGeneratorLoop loop) { PerimeterGeneratorLoops ret; for (PerimeterGeneratorLoop &child : loop.children) { @@ -2863,10 +2862,15 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( assert(thin_walls.empty()); ExtrusionEntityCollection children = this->_traverse_loops(loop.children, thin_walls, has_overhang ? 1 : count_since_overhang < 0 ? -1 : (count_since_overhang+1)); coll[idx.first] = nullptr; + bool has_steep_overhangs_this_loop = false; + if (loop.is_steep_overhang && this->layer->id() % 2 == 1 && !config->perimeter_reverse) { + has_steep_overhangs_this_loop = HasRoleVisitor::search(*eloop, HasThisRoleVisitor{erOverhangPerimeter}); + } if ((loop.is_contour && !reverse_contour) || (!loop.is_contour && reverse_hole)) { //note: this->layer->id() % 2 == 1 already taken into account in the is_steep_overhang compute (to save time). // if contour: reverse if steep_overhang & odd. if hole: the opposite - if ((loop.is_steep_overhang && this->layer->id() % 2 == 1) == loop.is_contour) + bool clockwise = ((config->perimeter_reverse || has_steep_overhangs_this_loop) && this->layer->id() % 2 == 1) == loop.is_contour; + if (clockwise) eloop->make_clockwise(); else eloop->make_counter_clockwise(); @@ -2885,8 +2889,9 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( coll_out.append(*eloop); } } else { + bool counter_clockwise = ((config->perimeter_reverse || has_steep_overhangs_this_loop) && this->layer->id() % 2 == 1) != loop.is_contour; // if hole: reverse if steep_overhang & odd. if contour: the opposite - if ((loop.is_steep_overhang && this->layer->id() % 2 == 1) != loop.is_contour) + if (counter_clockwise) eloop->make_counter_clockwise(); else eloop->make_clockwise(); @@ -3039,7 +3044,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_extrusions(std::vector

layer->id() % 2 == 1) to make_clockwise => need to detect is_steep_overhang on the arachne path - if (pg_extrusion.is_contour) + if ((config->perimeter_reverse /*|| pg_extrusion.is_steep_overhang*/ && this->layer->id() % 2 == 1) == pg_extrusion.is_contour) extrusion_loop.make_counter_clockwise(); else extrusion_loop.make_clockwise(); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 032b76b92b0..5d9fc3feffd 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -476,6 +476,7 @@ static std::vector s_Preset_print_options { "overhangs_width_speed", "overhangs_reverse", "overhangs_reverse_threshold", + "perimeter_reverse", "seam_position", "seam_angle_cost", "seam_notch_all", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 385c5d89841..300d5acf5d8 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4031,10 +4031,10 @@ void PrintConfigDef::init_fff_params() def = this->add("overhangs_reverse", coBool); def->label = L("Reverse on even"); - def->full_label = L("Overhang reversal"); + def->full_label = L("Overhang reversal on even layers"); def->category = OptionCategory::perimeter; - def->tooltip = L("Extrude perimeters that have a part over an overhang in the reverse direction on even layers (and not on odd layers like the first one)." - " This alternating pattern can drastically improve steep overhang." + def->tooltip = L("Extrude perimeters that have an overhanging part in the reverse direction on even layers (not on odd layers like the first one)." + " This alternating pattern can significantly improve steep overhangs." "\n!! this is a very slow algorithm (it uses the same results as extra_perimeters_overhangs) !!"); def->mode = comAdvancedE | comSuSi; def->set_default_value(new ConfigOptionBool(false)); @@ -4232,6 +4232,14 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comSuSi; def->set_default_value(new ConfigOptionPercent(100)); + def = this->add("perimeter_reverse", coBool); + def->label = L("Perimeter reversal on even layers"); + def->category = OptionCategory::perimeter; + def->tooltip = L("On even layers, all perimeter loops are reversed (it disables the overhang reversal, so it doesn't double-reverse)." + "That setting will likely create defects on the perimeters, so it's only useful is for materials that have some direction-dependent properties (stress lines)."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("perimeter_round_corners", coBool); def->label = L("Round corners"); def->full_label = L("Round corners for perimeters"); @@ -8204,6 +8212,7 @@ std::unordered_set prusa_export_to_remove_keys = { "perimeter_loop_seam", "perimeter_loop", "perimeter_overlap", +"perimeter_reverse", "perimeter_round_corners", "print_extrusion_multiplier", "print_custom_variables", @@ -8696,6 +8705,7 @@ void DynamicPrintConfig::normalize_fdm() this->opt("extra_perimeters_overhangs", true)->value = false; this->opt("extra_perimeters_odd_layers", true)->value = false; this->opt("overhangs_reverse", true)->value = false; + this->opt("perimeter_reverse", true)->value = false; } } @@ -9306,6 +9316,8 @@ std::string validate(const FullPrintConfig& cfg) return "Can't make more than one perimeter when spiral vase mode is enabled"; if (cfg.overhangs_reverse) return "Can't reverse the direction of the overhangs every layer when spiral vase mode is enabled"; + if (cfg.perimeter_reverse) + return "Can't reverse the direction of the perimeters every layer when spiral vase mode is enabled"; } // extrusion widths diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 6b491ae338a..a4d0a5334cf 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -903,6 +903,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, perimeter_loop)) ((ConfigOptionEnum, perimeter_loop_seam)) ((ConfigOptionPercent, perimeter_overlap)) + ((ConfigOptionBool, perimeter_reverse)) ((ConfigOptionFloatOrPercent, perimeter_speed)) // Total number of perimeters. ((ConfigOptionInt, perimeters)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ab143542a8b..67913ec4d11 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -852,6 +852,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "perimeter_extrusion_change_odd_layers" || opt_key == "perimeter_extrusion_spacing" || opt_key == "perimeter_extrusion_width" + || opt_key == "perimeter_reverse" || opt_key == "infill_overlap" || opt_key == "thin_perimeters" || opt_key == "thin_perimeters_all" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 8378e453796..a61f450473a 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -338,7 +338,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool have_perimeters = config->opt_int("perimeters") > 0; for (auto el : { "ensure_vertical_shell_thickness", "external_perimeter_speed", "extra_perimeters", "extra_perimeters_odd_layers", "external_perimeters_first", "external_perimeter_extrusion_width", "external_perimeter_extrusion_spacing","external_perimeter_extrusion_change_odd_layers", - "overhangs","perimeter_speed", + "overhangs", "perimeter_speed", "perimeter_reverse", "seam_position", "small_perimeter_speed", "small_perimeter_min_length", " small_perimeter_max_length", "spiral_vase", "perimeter_generator", "seam_notch_all", "seam_notch_inner", "seam_notch_outer"}) toggle_field(el, have_perimeters); @@ -356,13 +356,15 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("perimeter_bonding", config->opt_bool("external_perimeters_first") && !have_arachne && config->option("perimeter_overlap")->get_float() == 100.f && config->option("external_perimeter_overlap")->get_float() == 100.f); for (auto el : {"perimeter_loop", "extra_perimeters_overhangs", "no_perimeter_unsupported_algo", - "thin_perimeters", "overhangs_reverse", "perimeter_round_corners"}) + "thin_perimeters", "perimeter_round_corners"}) toggle_field(el, have_perimeters && !have_arachne); toggle_field("only_one_perimeter_top", have_perimeters); toggle_field("only_one_perimeter_first_layer", config->opt_int("perimeters") > 1); toggle_field("overhangs_width", config->option("overhangs_width_speed")->value > 0); - toggle_field("overhangs_reverse_threshold", have_perimeters && config->opt_bool("overhangs_reverse")); + bool have_overhangs_reverse = have_perimeters && !have_arachne && !config->opt_bool("perimeter_reverse"); + toggle_field("overhangs_reverse", have_overhangs_reverse); + toggle_field("overhangs_reverse_threshold", have_overhangs_reverse && config->opt_bool("overhangs_reverse")); toggle_field("overhangs_speed_enforce", have_perimeters && !config->opt_bool("perimeter_loop")); toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top")); toggle_field("thin_perimeters_all", have_perimeters && config->option("thin_perimeters")->get_float() != 0 && !have_arachne); From 8d2751646e6cfc23f013c7acbae4c30ad641125f Mon Sep 17 00:00:00 2001 From: Giuseppe Sorrentino Date: Wed, 28 Feb 2024 18:16:12 +0100 Subject: [PATCH 23/24] apple silicon build fix --- .github/workflows/ccpp_mac_arm.yml | 2 +- .github/workflows/ccpp_mac_arm_debug.yml | 2 +- .github/workflows/ccpp_mac_arm_rc.yml | 2 +- CMakeLists.txt | 1 + deps/TIFF/TIFF.cmake | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ccpp_mac_arm.yml b/.github/workflows/ccpp_mac_arm.yml index 710e70a29e1..090671cc17d 100644 --- a/.github/workflows/ccpp_mac_arm.yml +++ b/.github/workflows/ccpp_mac_arm.yml @@ -11,7 +11,7 @@ on: jobs: build: - runs-on: macos-11 + runs-on: macos-14 steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/ccpp_mac_arm_debug.yml b/.github/workflows/ccpp_mac_arm_debug.yml index eabd6ecd364..10b4e1ce387 100644 --- a/.github/workflows/ccpp_mac_arm_debug.yml +++ b/.github/workflows/ccpp_mac_arm_debug.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: macos-11 + runs-on: macos-14 steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/ccpp_mac_arm_rc.yml b/.github/workflows/ccpp_mac_arm_rc.yml index 9cc5bd1df7c..5b68de5f80c 100644 --- a/.github/workflows/ccpp_mac_arm_rc.yml +++ b/.github/workflows/ccpp_mac_arm_rc.yml @@ -8,7 +8,7 @@ on: jobs: build: - runs-on: macos-11 + runs-on: macos-14 steps: - uses: actions/checkout@v2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 18f26baf813..a112949bba4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,6 +206,7 @@ if(WIN32) endif() if (APPLE) + add_compile_options(-D_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION) message("OS X SDK Path: ${CMAKE_OSX_SYSROOT}") if (CMAKE_OSX_DEPLOYMENT_TARGET) message("OS X Deployment Target: ${CMAKE_OSX_DEPLOYMENT_TARGET}") diff --git a/deps/TIFF/TIFF.cmake b/deps/TIFF/TIFF.cmake index d8ef178b182..3948239fb30 100644 --- a/deps/TIFF/TIFF.cmake +++ b/deps/TIFF/TIFF.cmake @@ -6,7 +6,7 @@ if (APPLE) if (${CMAKE_OSX_ARCHITECTURES} MATCHES "arm") prusaslicer_add_cmake_project(TIFF URL https://gitlab.com/libtiff/libtiff/-/archive/v4.3.0/libtiff-v4.3.0.zip - URL_HASH SHA256=a1db6826ea1b8b08eaf168973abb29b8a143fb744c1b24cc6967cb12f5e6e81f + URL_HASH SHA256=4fca1b582c88319f3ad6ecd5b46320eadaf5eb4ef6f6c32d44caaae4a03d0726 DEPENDS ${ZLIB_PKG} ${PNG_PKG} dep_JPEG CMAKE_ARGS -Dlzma:BOOL=OFF From fe198c25cddb1f5d83f0c96f87ef2610b078ba49 Mon Sep 17 00:00:00 2001 From: supermerill Date: Mon, 18 Mar 2024 11:22:54 +0100 Subject: [PATCH 24/24] move freq settings definition to their own (strip-down) tab. add vector get/set for script fix enum set_string for script allow \: in ui files better script debug --- resources/ui_layout/default/freq_fff.as | 97 ++++ .../default/{sla_print.as => freq_sla.as} | 0 src/libslic3r/Config.hpp | 10 +- src/libslic3r/Preset.cpp | 6 + src/libslic3r/PrintConfig.cpp | 33 ++ src/libslic3r/PrintConfig.hpp | 30 ++ src/slic3r/GUI/GUI_App.cpp | 18 +- src/slic3r/GUI/GUI_App.hpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 7 +- src/slic3r/GUI/OG_CustomCtrl.cpp | 4 +- src/slic3r/GUI/OptionsGroup.cpp | 14 +- src/slic3r/GUI/OptionsGroup.hpp | 24 +- src/slic3r/GUI/Plater.cpp | 60 +-- src/slic3r/GUI/PresetComboBoxes.cpp | 31 +- src/slic3r/GUI/PresetComboBoxes.hpp | 8 +- src/slic3r/GUI/ScriptExecutor.cpp | 479 +++++++++++++----- src/slic3r/GUI/ScriptExecutor.hpp | 64 +-- src/slic3r/GUI/Search.cpp | 3 +- src/slic3r/GUI/Tab.cpp | 280 ++++++---- src/slic3r/GUI/Tab.hpp | 87 ++-- src/slic3r/GUI/UnsavedChangesDialog.cpp | 6 +- 21 files changed, 915 insertions(+), 348 deletions(-) create mode 100644 resources/ui_layout/default/freq_fff.as rename resources/ui_layout/default/{sla_print.as => freq_sla.as} (100%) diff --git a/resources/ui_layout/default/freq_fff.as b/resources/ui_layout/default/freq_fff.as new file mode 100644 index 00000000000..020b2a8a144 --- /dev/null +++ b/resources/ui_layout/default/freq_fff.as @@ -0,0 +1,97 @@ +// quick settings brim + +float last_brim_val = 5; + +int s_brim_get() +{ + float bw = get_float("brim_width"); + if (bw > 0) { + last_brim_val = bw; + return 1; + } + return 0; +} + +void s_brim_set(bool new_val) +{ + if(new_val) { + float bw = get_float("brim_width"); + set_float("brim_width", last_brim_val); + } else { + set_float("brim_width", 0); + } +} + +// quick settings support + +int s_support_fff_get(string &out get_val) +{ + bool support_material = get_bool("support_material"); + if (!support_material) { // None + return 0; + } + bool support_material_auto = get_bool("support_material_auto"); + if (!support_material_auto) { // For support enforcers only + return 2; + } + bool support_material_buildplate_only = get_bool("support_material_buildplate_only"); + if (support_material_buildplate_only) { // Support on build plate only + return 1; + } + // everywhere + return 3; +} + +void s_support_fff_set(string &in new_val, int idx) +{ + if(idx == 0) { // None + back_initial_value("support_material_buildplate_only"); + back_initial_value("support_material_auto"); + set_bool("support_material", false); + } else if(idx == 1) { // Support on build plate only + set_bool("support_material_buildplate_only", true); + set_bool("support_material_auto", true); + set_bool("support_material", true); + } else if(idx == 2) { // For support enforcers only + set_bool("support_material_buildplate_only", false); + set_bool("support_material_auto", false); + set_bool("support_material", true); + } else if(idx == 3) { // everywhere + set_bool("support_material_buildplate_only", false); + set_bool("support_material_auto", true); + set_bool("support_material", true); + } +} + + +// quick settings bed type (nematx) + +int s_bed_fff_get(string &out get_val) +{ + int bed_temperature = get_int("bed_temperature"); + int fl_bed_temperature = get_int("first_layer_bed_temperature"); + if (bed_temperature == fl_bed_temperature) { + if (bed_temperature == 130) { + return 1; //glue + } + if (bed_temperature == 170) { + return 2; //noglue + } + } + return 0; // custom +} + +void s_bed_fff_set(string &in new_val, int idx) +{ + if(idx == 0) { // custom + back_initial_value("bed_temperature"); + back_initial_value("first_layer_bed_temperature"); + } else if(idx == 1) { // glue + set_int("bed_temperature", 130); + set_int("first_layer_bed_temperature", 130); + } else if(idx == 2) { // noglue + set_int("bed_temperature", 170); + set_int("first_layer_bed_temperature", 170); + } +} + diff --git a/resources/ui_layout/default/sla_print.as b/resources/ui_layout/default/freq_sla.as similarity index 100% rename from resources/ui_layout/default/sla_print.as rename to resources/ui_layout/default/freq_sla.as diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 653537cf466..55c689566a6 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2245,8 +2245,9 @@ class ConfigBase : public ConfigOptionResolver // but it carries the defaults of the configuration values. ConfigBase() = default; +#ifndef _DEBUG ~ConfigBase() override = default; - +#endif // to get to the config more generic than this one, if available const ConfigBase* parent = nullptr; @@ -2382,6 +2383,13 @@ class ConfigBase : public ConfigOptionResolver static std::map load_gcode_string_legacy(const char* str); static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions); +#ifdef _DEBUG + //little dirty test to be sure it exists (not needed, but it's good for testing) + int32_t m_exists = 0x55555555; + bool exists() { return m_exists == 0x55555555; } + ~ConfigBase() override { m_exists = 0; } +#endif + private: // Set a configuration value from a string. bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 5d9fc3feffd..3fae1662a82 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1742,6 +1742,8 @@ std::string Preset::type_name(Type t) { case Preset::TYPE_SLA_PRINT: return "sla_print"; case Preset::TYPE_SLA_MATERIAL: return "sla_material"; case Preset::TYPE_PRINTER: return "printer"; + case Preset::TYPE_FREQUENT_FFF: return "freq_fff"; + case Preset::TYPE_FREQUENT_SLA: return "freq_sla"; default: return "invalid"; } } @@ -1757,6 +1759,10 @@ Preset::Type Preset::type_from_name(std::string name) { return Preset::TYPE_SLA_MATERIAL; if ("printer" == name) return Preset::TYPE_PRINTER; + if ("freq_fff" == name) + return Preset::TYPE_FREQUENT_FFF; + if ("freq_sla" == name) + return Preset::TYPE_FREQUENT_SLA; return Preset::TYPE_INVALID; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 300d5acf5d8..72c04ae9bfd 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -8524,6 +8524,39 @@ DynamicPrintConfig* DynamicPrintConfig::new_from_defaults_keys(const std::vector return out; } +const ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_key) const +{ + for (auto conf : storages) { + assert(conf->exists()); + const ConfigOption *opt = conf->optptr(opt_key); + if (opt) + return opt; + } + return nullptr; +} +ConfigOption *MultiPtrPrintConfig::optptr(const t_config_option_key &opt_key, bool create) +{ + assert(!create); + for (auto conf : storages) { + assert(conf->exists()); + ConfigOption *opt = conf->optptr(opt_key); + if (opt) + return opt; + } + return nullptr; +} +t_config_option_keys MultiPtrPrintConfig::keys() const +{ + assert(false); + // shouldn't need ot call that + t_config_option_keys keys; + for (auto conf : storages) { + assert(conf->exists()); + append(keys, conf->keys()); + } + return keys; +} + PrinterTechnology printer_technology(const ConfigBase& cfg) { const ConfigOptionEnum* opt = cfg.option>("printer_technology"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index a4d0a5334cf..6c3075a6ba3 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -404,6 +404,27 @@ class DynamicPrintConfig : public DynamicConfig std::set update_phony(const std::vector config_collection, bool exclude_default_extrusion = false); }; +// An indirection to a bunch of Config +class MultiPtrPrintConfig : public virtual ConfigBase +{ +public: + MultiPtrPrintConfig() = default; + + // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. + const ConfigDef* def() const override { return &print_config_def; } + + // Overrides ConfigResolver::optptr(). + const ConfigOption* optptr(const t_config_option_key &opt_key) const override; + // Overrides ConfigBase::optptr(). Find ando/or create a ConfigOption instance for a given name. + ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) override; + // Overrides ConfigBase::keys(). Collect names of all configuration values maintained by this configuration store. + t_config_option_keys keys() const override; + + + std::vector storages; +private: +}; + void handle_legacy_sla(DynamicPrintConfig& config); class StaticPrintConfig : public StaticConfig @@ -1639,6 +1660,15 @@ class ModelConfig bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; } template void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } + void set_any(const std::string &opt_key, boost::any value, int16_t extruder_id) + { + ConfigOption *opt = m_data.option(opt_key, true); + assert(opt); + if (opt) { + opt->set_any(value, extruder_id); + this->touch(); + } + } void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false) { m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); } bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 642794af3eb..8e119bd3e09 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -2442,11 +2442,11 @@ bool GUI_App::load_language(wxString language, bool initial) return true; } -Tab* GUI_App::get_tab(Preset::Type type) +Tab* GUI_App::get_tab(Preset::Type type, bool only_completed) { for (Tab* tab: tabs_list) if (tab->type() == type) - return tab->completed() ? tab : nullptr; // To avoid actions with no-completed Tab + return tab->completed() || !only_completed ? tab : nullptr; // To avoid actions with no-completed Tab return nullptr; } @@ -2757,7 +2757,7 @@ bool GUI_App::has_unsaved_preset_changes() const { PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); for (const Tab* const tab : tabs_list) { - if (tab->supports_printer_technology(printer_technology) && tab->saved_preset_is_dirty()) + if (tab->supports_printer_technology(printer_technology) && tab->completed() && tab->saved_preset_is_dirty()) return true; } return false; @@ -2767,7 +2767,7 @@ bool GUI_App::has_current_preset_changes() const { PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); for (const Tab* const tab : tabs_list) { - if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + if (tab->supports_printer_technology(printer_technology) && tab->completed() && tab->current_preset_is_dirty()) return true; } return false; @@ -2777,7 +2777,7 @@ void GUI_App::update_saved_preset_from_current_preset() { PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); for (Tab* tab : tabs_list) { - if (tab->supports_printer_technology(printer_technology)) + if (tab->supports_printer_technology(printer_technology) && tab->completed()) tab->update_saved_preset_from_current_preset(); } } @@ -2787,8 +2787,10 @@ std::vector GUI_App::get_active_preset_collections() co std::vector ret; PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); for (const Tab* tab : tabs_list) - if (tab->supports_printer_technology(printer_technology)) + if (tab->supports_printer_technology(printer_technology) && tab->completed()) { + assert(tab->get_presets()); ret.push_back(tab->get_presets()); + } return ret; } @@ -2874,7 +2876,7 @@ bool GUI_App::check_and_keep_current_preset_changes(const wxString& caption, con PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); for (const Tab* const tab : tabs_list) { - if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + if (tab->supports_printer_technology(printer_technology) && tab->completed() && tab->current_preset_is_dirty()) tab->m_presets->discard_current_changes(); } load_current_presets(false); @@ -2989,7 +2991,7 @@ void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/) PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); this->plater()->set_printer_technology(printer_technology); for (Tab *tab : tabs_list) - if (tab->supports_printer_technology(printer_technology)) { + if (tab->supports_printer_technology(printer_technology) && tab->get_presets()) { if (tab->type() == Preset::TYPE_PRINTER) { static_cast(tab)->update_pages(); // Mark the plater to update print bed by tab->load_current_preset() from Plater::on_config_change(). diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 085da89e0c9..3dc7f0c9081 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -268,7 +268,7 @@ class GUI_App : public wxApp bool switch_language(); bool load_language(wxString language, bool initial); - Tab* get_tab(Preset::Type type); + Tab* get_tab(Preset::Type type, bool only_completed = true); ConfigOptionMode get_mode(); void save_mode(const ConfigOptionMode mode) ; void update_mode(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 82b52fb6f32..742cd695d32 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1264,6 +1264,10 @@ void MainFrame::create_preset_tabs() add_created_tab(new TabSLAPrint(m_tabpanel)); add_created_tab(new TabSLAMaterial(m_tabpanel)); add_created_tab(new TabPrinter(m_tabpanel)); + TabFrequent* freq = (new TabFrequent(m_tabpanel, "Freq_fff", Preset::Type::TYPE_FREQUENT_FFF)); + freq->create_preset_tab(); + freq = (new TabFrequent(m_tabpanel, "Freq_sla", Preset::Type::TYPE_FREQUENT_SLA)); + freq->create_preset_tab(); } void MainFrame::add_created_tab(Tab* panel) @@ -2868,7 +2872,8 @@ void MainFrame::update_ui_from_settings() if (m_plater) m_plater->update_ui_from_settings(); for (auto tab: wxGetApp().tabs_list) - tab->update_ui_from_settings(); + if(tab->completed()) + tab->update_ui_from_settings(); } std::string MainFrame::get_base_name(const wxString &full_name, const char *extension) const diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index c4fb6e0988b..034eaa58901 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -583,10 +583,10 @@ void OG_CustomCtrl::CtrlLine::update_visibility(ConfigOptionMode mode) if (og_line.is_separator()) return; const std::vector