From b52a9de50916c5407105b8688e0383d5582a8525 Mon Sep 17 00:00:00 2001 From: "qing.zhang" Date: Mon, 15 Jul 2024 11:02:12 +0800 Subject: [PATCH] ENH: smooth z dir speed and layer time Jira: none generate continuitious outer wall speed in z dir generate continuitious layer time in z dir Signed-off-by: qing.zhang Change-Id: I0ade543d2f9ee40f5cd65c533eb261d85e5eaa34 --- src/libslic3r/ExtrusionEntity.hpp | 18 + src/libslic3r/ExtrusionEntityCollection.hpp | 8 +- src/libslic3r/GCode.cpp | 228 +++++++++-- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/GCode/CoolingBuffer.cpp | 404 ++++++++++---------- src/libslic3r/GCode/CoolingBuffer.hpp | 223 ++++++++++- src/libslic3r/GCode/Smoothing.hpp | 219 +++++++++++ src/libslic3r/Layer.cpp | 125 +++++- src/libslic3r/Layer.hpp | 6 +- src/libslic3r/LayerRegion.cpp | 6 +- src/libslic3r/PerimeterGenerator.cpp | 54 +++ src/libslic3r/PerimeterGenerator.hpp | 15 +- src/libslic3r/Preset.cpp | 3 +- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintConfig.cpp | 14 + src/libslic3r/PrintConfig.hpp | 2 + src/libslic3r/PrintObject.cpp | 88 ++++- src/slic3r/GUI/ConfigManipulation.cpp | 4 +- src/slic3r/GUI/Tab.cpp | 2 + 19 files changed, 1158 insertions(+), 265 deletions(-) create mode 100644 src/libslic3r/GCode/Smoothing.hpp diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index e32c6d3c0..312c990e7 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -16,6 +16,24 @@ using ExPolygons = std::vector; class ExtrusionEntityCollection; class Extruder; +struct LoopNode +{ + //store outer wall and mark if it's loop + std::pair exp; + int node_id; + int region_id; + int perimeter_id; + int loop_id = 0; + BoundingBox bbox; + int merged_id = -1; + + //upper loop info + std::vector upper_node_id; + + //lower loop info + std::vector lower_node_id; +}; + // Each ExtrusionRole value identifies a distinct set of { extruder, speed } enum ExtrusionRole : uint8_t { erNone, diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 413834db2..dec981eb8 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -31,9 +31,12 @@ public: ExtrusionEntitiesPtr entities; // we own these entities bool no_sort; + + std::pair loop_node_range; ExtrusionEntityCollection(): no_sort(false) {} - ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort), is_reverse(other.is_reverse) { this->append(other.entities); } - ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort), is_reverse(other.is_reverse) {} + ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort), is_reverse(other.is_reverse), loop_node_range(other.loop_node_range) { this->append(other.entities); } + ExtrusionEntityCollection(ExtrusionEntityCollection &&other) + : entities(std::move(other.entities)), no_sort(other.no_sort), is_reverse(other.is_reverse), loop_node_range(other.loop_node_range) {} explicit ExtrusionEntityCollection(const ExtrusionPaths &paths); ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other); ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) @@ -41,6 +44,7 @@ public: this->entities = std::move(other.entities); this->no_sort = other.no_sort; is_reverse = other.is_reverse; + loop_node_range = other.loop_node_range; return *this; } ~ExtrusionEntityCollection() { clear(); } diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8606b295c..aac9ba79f 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -67,6 +67,7 @@ using namespace std::literals::string_view_literals; #endif #include +#include namespace Slic3r { @@ -2405,8 +2406,18 @@ void GCode::process_layers( const std::vector>> &layers_to_print, GCodeOutputStream &output_stream) { - // The pipeline is variable: The vase mode filter is optional. + //BBS: get object label id size_t layer_to_print_idx = 0; + std::vector object_label; + + for (const PrintInstance *instance : print_object_instances_ordering) + object_label.push_back(instance->model_instance->get_labeled_id()); + + SmoothCalculator smooth_calculator(layers_to_print.size(), print.objects().size()); + std::vector> layers_extruder_adjustments; + layers_extruder_adjustments.resize(layers_to_print.size()); + + // The pipeline is variable: The vase mode filter is optional. const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> GCode::LayerResult { if (layer_to_print_idx == layers_to_print.size()) { @@ -2435,19 +2446,72 @@ void GCode::process_layers( bool last_layer = in.layer_id == layers_to_print.size() - 1; return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush}; }); - const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string { - return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); - }); - const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&output_stream](std::string s) { output_stream.write(s); } - ); + std::vector layers_results; + layers_results.resize(layers_to_print.size()); + const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_results](GCode::LayerResult in) { + layers_results[in.layer_id] = in; + std::string out = cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator, layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, false); + if (in.layer_id == 0) + layers_results[in.layer_id].gcode = out; + return; + }); + + const auto cooling_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [ &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments](GCode::LayerResult in) -> std::string { + return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator, layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, true); + }); + + // BBS: apply new feedrate of outwall and recalculate layer time + int layer_idx = 0; + bool layer_time_smoothing = m_config.layer_time_smoothing; + const auto cooling_smooth= tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [&cooling_buffer = *this->m_cooling_buffer.get(), &layer_idx, object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_to_print, &layers_results, layer_time_smoothing](tbb::flow_control& fc) -> std::string { + if(layer_idx == layers_to_print.size()){ + fc.stop(); + return{}; + }else{ + if (layer_idx > 0) { + const std::pair> &layer = layers_to_print[layer_idx++]; + return cooling_buffer.apply_smooth_layer_time(std::move(layers_results[layer_idx - 1].gcode), smooth_calculator, layers_extruder_adjustments[layer_idx - 1], layer_idx - 1, layer_time_smoothing); + } else { + return layers_results[layer_idx++].gcode; + } + } + }); + + + const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&output_stream](std::string s) { output_stream.write(s); }); + + // BBS: apply cooling // The pipeline elements are joined using const references, thus no copying is performed. if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_mode & cooling & output); - else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, generator & spiral_mode & cooling_vase & output); + else if (!(layer_time_smoothing || m_config.z_direction_outwall_speed_continuous)) + tbb::parallel_pipeline(12, generator & cooling_vase & output); + else { + tbb::parallel_pipeline(12, generator & cooling); + + if (m_config.z_direction_outwall_speed_continuous) { + smooth_calculator.smooth_layer_speed(); + + tbb::parallel_for(tbb::blocked_range(0, layers_to_print.size()), [this, &print, &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_to_print, &layers_results](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + print.throw_if_canceled(); + if (layer_idx > 0) { + const std::pair> &layer = layers_to_print[layer_idx]; + cooling_buffer.apply_smooth_speed(smooth_calculator, layers_extruder_adjustments[layer_idx], layer_idx); + } + } + }); + } + //smooth layer time + if (layer_time_smoothing) + smooth_calculator.smooth_layer_time(); + + tbb::parallel_pipeline(12, cooling_smooth & output); + } } // Process all layers of a single object instance (sequential mode) with a parallel pipeline: @@ -2462,8 +2526,17 @@ void GCode::process_layers( // BBS const bool prime_extruder) { + // BBS: get object label id + size_t layer_to_print_idx = 0; + std::vector object_label; + for (LayerToPrint layer : layers_to_print) + object_label.push_back(layer.original_object->instances()[single_object_idx].model_instance->get_labeled_id()); + + SmoothCalculator smooth_calculator(layers_to_print.size(), print.objects().size()); + std::vector> layers_extruder_adjustments; + layers_extruder_adjustments.resize(layers_to_print.size()); + // The pipeline is variable: The vase mode filter is optional. - size_t layer_to_print_idx = 0; const auto generator = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx, prime_extruder](tbb::flow_control& fc) -> GCode::LayerResult { if (layer_to_print_idx == layers_to_print.size()) { @@ -2489,19 +2562,75 @@ void GCode::process_layers( bool last_layer = in.layer_id == layers_to_print.size() - 1; return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush }; }); - const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string { - return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush); - }); - const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&output_stream](std::string s) { output_stream.write(s); } - ); + //BBS: get objects and nodes info, for better arrange + const ConstPrintObjectPtrsAdaptor &objects = print.objects(); + + std::vector layers_results; + layers_results.resize(layers_to_print.size()); + const auto cooling = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&cooling_buffer = *this->m_cooling_buffer.get(), &smooth_calculator, &layers_extruder_adjustments, object_label, &layers_results](GCode::LayerResult in) { + layers_results[in.layer_id] = in; + std::string out = cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator, layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, false); + if (in.layer_id == 0) + layers_results[in.layer_id].gcode = out; + return; + }); + + const auto cooling_vase = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [ &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments](GCode::LayerResult in) -> std::string { + return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator,layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, true); + }); + + // BBS: apply new feedrate of outwall and recalculate layer time + int layer_idx = 0; + bool layer_time_smoothing = m_config.layer_time_smoothing; + const auto cooling_smooth= tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, [&cooling_buffer = *this->m_cooling_buffer.get(), &layer_idx, object_label, &layers_to_print, &layers_results, layer_time_smoothing, &smooth_calculator, &layers_extruder_adjustments](tbb::flow_control& fc) -> std::string { + if(layer_idx == layers_to_print.size()){ + fc.stop(); + return{}; + }else{ + if (layer_idx > 0) { + LayerToPrint &layer = layers_to_print[layer_idx++]; + return cooling_buffer.apply_smooth_layer_time(std::move(layers_results[layer_idx - 1].gcode), smooth_calculator, layers_extruder_adjustments[layer_idx - 1], layer_idx - 1, layer_time_smoothing); + } else { + return layers_results[layer_idx++].gcode; + } + } + }); + + + const auto output = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, + [&output_stream](std::string s) { output_stream.write(s); }); + + // BBS: apply cooling // The pipeline elements are joined using const references, thus no copying is performed. if (m_spiral_vase) - tbb::parallel_pipeline(12, generator & spiral_mode & cooling & output); - else - tbb::parallel_pipeline(12, generator & cooling & output); + tbb::parallel_pipeline(12, generator & spiral_mode & cooling_vase & output); + else if (!(layer_time_smoothing || m_config.z_direction_outwall_speed_continuous)) + tbb::parallel_pipeline(12, generator & cooling_vase & output); + else { + tbb::parallel_pipeline(12, generator & cooling); + + if (m_config.z_direction_outwall_speed_continuous) { + smooth_calculator.smooth_layer_speed(); + + tbb::parallel_for(tbb::blocked_range(0, layers_to_print.size()), [this, &print, &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_to_print, &layers_results](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + print.throw_if_canceled(); + if (layer_idx > 0) { + LayerToPrint &layer = layers_to_print[layer_idx++]; + cooling_buffer.apply_smooth_speed(smooth_calculator, layers_extruder_adjustments[layer_idx], layer_idx); + } + } + }); + } + // smooth layer time + if (layer_time_smoothing) + smooth_calculator.smooth_layer_time(); + + tbb::parallel_pipeline(12, cooling_smooth & output); + } } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) @@ -3364,7 +3493,8 @@ GCode::LayerResult GCode::process_layer( // The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice: std::vector printing_extruders; for (const ObjectByExtruder::Island::Region::Type entity_type : { ObjectByExtruder::Island::Region::INFILL, ObjectByExtruder::Island::Region::PERIMETERS }) { - for (const ExtrusionEntity *ee : (entity_type == ObjectByExtruder::Island::Region::INFILL) ? layerm->fills.entities : layerm->perimeters.entities) { + bool is_infill = entity_type == ObjectByExtruder::Island::Region::INFILL; + for (const ExtrusionEntity *ee : is_infill ? layerm->fills.entities : layerm->perimeters.entities) { // extrusions represents infill or perimeter extrusions of a single island. assert(dynamic_cast(ee) != nullptr); const auto *extrusions = static_cast(ee); @@ -3417,6 +3547,15 @@ GCode::LayerResult GCode::process_layer( if (islands[island_idx].by_region.empty()) islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region()); islands[island_idx].by_region[region.print_region_id()].append(entity_type, extrusions, entity_overrides); + int start = extrusions->loop_node_range.first; + int end = extrusions->loop_node_range.second; + //BBS: add merged node infor + if (!is_infill) { + for (; start < end; ++start) { + const LoopNode *node = &layer.loop_nodes[start]; + islands[island_idx].by_region[region.print_region_id()].merged_node.emplace_back(node); + } + } break; } } @@ -3551,6 +3690,8 @@ GCode::LayerResult GCode::process_layer( if (m_config.reduce_crossing_wall) m_avoid_crossing_perimeters.init_layer(*m_layer); + //BBS: label object id, prepare for cooling + gcode += "; OBJECT_ID: " + std::to_string(instance_to_print.label_object_id) + "\n"; std::string temp_start_str; if (m_enable_label_object) { std::string start_str = std::string("; start printing object, unique label id: ") + std::to_string(instance_to_print.label_object_id) + "\n"; @@ -4268,8 +4409,18 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorloop_id) { + gcode += "; COOLING_NODE: " + std::to_string(region.merged_node[curr_node]->merged_id) + "\n"; + curr_node++; + } + gcode += this->extrude_entity(*ee, "perimeter", -1.); + } } return gcode; } @@ -4894,26 +5045,29 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } std::string comment; + bool cooling_extrude = false; if (m_enable_cooling_markers) { if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) { //BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ? Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1; - if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter)) { - gcode += ";_OVERHANG_FAN_START\n"; - } else if (path.get_overhang_degree() > overhang_threshold || - is_bridge(path.role())) + if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter) || (path.get_overhang_degree() > overhang_threshold || + is_bridge(path.role()))) { gcode += ";_OVERHANG_FAN_START\n"; + } } int overhang_boundary_for_cooling = EXTRUDER_CONFIG(overhang_threshold_participating_cooling); if (!is_bridge(path.role()) && path.get_overhang_degree() <= overhang_boundary_for_cooling) { + cooling_extrude = true; comment = ";_EXTRUDE_SET_SPEED"; } if (path.role() == erExternalPerimeter) comment += ";_EXTERNAL_PERIMETER"; + else if (path.role() == erPerimeter) + comment += ";_PERIMETER"; } // F is mm per minute. @@ -5003,22 +5157,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } } if (m_enable_cooling_markers) { - if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) { + if (cooling_extrude) + gcode += ";_EXTRUDE_END\n"; + + if ( EXTRUDER_CONFIG(enable_overhang_bridge_fan)) { //BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ? Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1; - if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter)) { - gcode += ";_EXTRUDE_END\n"; + if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter) || (path.get_overhang_degree() > overhang_threshold || + is_bridge(path.role()))) gcode += ";_OVERHANG_FAN_END\n"; - - } else if (path.get_overhang_degree() > overhang_threshold || - is_bridge(path.role())) - gcode += ";_OVERHANG_FAN_END\n"; - else - gcode += ";_EXTRUDE_END\n"; - } - else { - gcode += ";_EXTRUDE_END\n"; } } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index f3096a74b..98b9dedb0 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -366,7 +366,7 @@ private: ExtrusionEntitiesPtr perimeters; // Non-owned references to LayerRegion::fills::entities ExtrusionEntitiesPtr infills; - + std::vector merged_node; std::vector infills_overrides; std::vector perimeters_overrides; diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 1d35b2ec5..4d81a4853 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -41,202 +41,6 @@ void CoolingBuffer::reset(const Vec3d &position) m_current_fan_speed = -1; } -struct CoolingLine -{ - enum Type { - TYPE_SET_TOOL = 1 << 0, - TYPE_EXTRUDE_END = 1 << 1, - TYPE_OVERHANG_FAN_START = 1 << 2, - TYPE_OVERHANG_FAN_END = 1 << 3, - TYPE_G0 = 1 << 4, - TYPE_G1 = 1 << 5, - TYPE_ADJUSTABLE = 1 << 6, - TYPE_EXTERNAL_PERIMETER = 1 << 7, - // The line sets a feedrate. - TYPE_HAS_F = 1 << 8, - TYPE_WIPE = 1 << 9, - TYPE_G4 = 1 << 10, - TYPE_G92 = 1 << 11, - //BBS: add G2 G3 type - TYPE_G2 = 1 << 12, - TYPE_G3 = 1 << 13, - TYPE_FORCE_RESUME_FAN = 1 << 14, - TYPE_SET_FAN_CHANGING_LAYER = 1 << 15, - }; - - CoolingLine(unsigned int type, size_t line_start, size_t line_end) : - type(type), line_start(line_start), line_end(line_end), - length(0.f), feedrate(0.f), time(0.f), time_max(0.f), slowdown(false) {} - - bool adjustable(bool slowdown_external_perimeters) const { - return (this->type & TYPE_ADJUSTABLE) && - (! (this->type & TYPE_EXTERNAL_PERIMETER) || slowdown_external_perimeters) && - this->time < this->time_max; - } - - bool adjustable() const { - return (this->type & TYPE_ADJUSTABLE) && this->time < this->time_max; - } - - size_t type; - // Start of this line at the G-code snippet. - size_t line_start; - // End of this line at the G-code snippet. - size_t line_end; - // XY Euclidian length of this segment. - float length; - // Current feedrate, possibly adjusted. - float feedrate; - // Current duration of this segment. - float time; - // Maximum duration of this segment. - float time_max; - // If marked with the "slowdown" flag, the line has been slowed down. - bool slowdown; -}; - -// Calculate the required per extruder time stretches. -struct PerExtruderAdjustments -{ - // Calculate the total elapsed time per this extruder, adjusted for the slowdown. - float elapsed_time_total() const { - float time_total = 0.f; - for (const CoolingLine &line : lines) - time_total += line.time; - return time_total; - } - // Calculate the total elapsed time when slowing down - // to the minimum extrusion feed rate defined for the current material. - float maximum_time_after_slowdown(bool slowdown_external_perimeters) const { - float time_total = 0.f; - for (const CoolingLine &line : lines) - if (line.adjustable(slowdown_external_perimeters)) { - if (line.time_max == FLT_MAX) - return FLT_MAX; - else - time_total += line.time_max; - } else - time_total += line.time; - return time_total; - } - // Calculate the adjustable part of the total time. - float adjustable_time(bool slowdown_external_perimeters) const { - float time_total = 0.f; - for (const CoolingLine &line : lines) - if (line.adjustable(slowdown_external_perimeters)) - time_total += line.time; - return time_total; - } - // Calculate the non-adjustable part of the total time. - float non_adjustable_time(bool slowdown_external_perimeters) const { - float time_total = 0.f; - for (const CoolingLine &line : lines) - if (! line.adjustable(slowdown_external_perimeters)) - time_total += line.time; - return time_total; - } - // Slow down the adjustable extrusions to the minimum feedrate allowed for the current extruder material. - // Used by both proportional and non-proportional slow down. - float slowdown_to_minimum_feedrate(bool slowdown_external_perimeters) { - float time_total = 0.f; - for (CoolingLine &line : lines) { - if (line.adjustable(slowdown_external_perimeters)) { - assert(line.time_max >= 0.f && line.time_max < FLT_MAX); - line.slowdown = true; - line.time = line.time_max; - line.feedrate = line.length / line.time; - } - time_total += line.time; - } - return time_total; - } - // Slow down each adjustable G-code line proportionally by a factor. - // Used by the proportional slow down. - float slow_down_proportional(float factor, bool slowdown_external_perimeters) { - assert(factor >= 1.f); - float time_total = 0.f; - for (CoolingLine &line : lines) { - if (line.adjustable(slowdown_external_perimeters)) { - line.slowdown = true; - line.time = std::min(line.time_max, line.time * factor); - line.feedrate = line.length / line.time; - } - time_total += line.time; - } - return time_total; - } - - // Sort the lines, adjustable first, higher feedrate first. - // Used by non-proportional slow down. - void sort_lines_by_decreasing_feedrate() { - std::sort(lines.begin(), lines.end(), [](const CoolingLine &l1, const CoolingLine &l2) { - bool adj1 = l1.adjustable(); - bool adj2 = l2.adjustable(); - return (adj1 == adj2) ? l1.feedrate > l2.feedrate : adj1; - }); - for (n_lines_adjustable = 0; - n_lines_adjustable < lines.size() && this->lines[n_lines_adjustable].adjustable(); - ++ n_lines_adjustable); - time_non_adjustable = 0.f; - for (size_t i = n_lines_adjustable; i < lines.size(); ++ i) - time_non_adjustable += lines[i].time; - } - - // Calculate the maximum time stretch when slowing down to min_feedrate. - // Slowdown to min_feedrate shall be allowed for this extruder's material. - // Used by non-proportional slow down. - float time_stretch_when_slowing_down_to_feedrate(float min_feedrate) const { - float time_stretch = 0.f; - assert(this->slow_down_min_speed < min_feedrate + EPSILON); - for (size_t i = 0; i < n_lines_adjustable; ++ i) { - const CoolingLine &line = lines[i]; - if (line.feedrate > min_feedrate) - time_stretch += line.time * (line.feedrate / min_feedrate - 1.f); - } - return time_stretch; - } - - // Slow down all adjustable lines down to min_feedrate. - // Slowdown to min_feedrate shall be allowed for this extruder's material. - // Used by non-proportional slow down. - void slow_down_to_feedrate(float min_feedrate) { - assert(this->slow_down_min_speed < min_feedrate + EPSILON); - for (size_t i = 0; i < n_lines_adjustable; ++ i) { - CoolingLine &line = lines[i]; - if (line.feedrate > min_feedrate) { - line.time *= std::max(1.f, line.feedrate / min_feedrate); - line.feedrate = min_feedrate; - line.slowdown = true; - } - } - } - - // Extruder, for which the G-code will be adjusted. - unsigned int extruder_id = 0; - // Is the cooling slow down logic enabled for this extruder's material? - bool cooling_slow_down_enabled = false; - // Slow down the print down to slow_down_min_speed if the total layer time is below slow_down_layer_time. - float slow_down_layer_time = 0.f; - // Minimum print speed allowed for this extruder. - float slow_down_min_speed = 0.f; - - // Parsed lines. - std::vector lines; - // The following two values are set by sort_lines_by_decreasing_feedrate(): - // Number of adjustable lines, at the start of lines. - size_t n_lines_adjustable = 0; - // Non-adjustable time of lines starting with n_lines_adjustable. - float time_non_adjustable = 0; - // Current total time for this extruder. - float time_total = 0; - // Maximum time for this extruder, when the maximum slow down is applied. - float time_maximum = 0; - - // Temporaries for processing the slow down. Both thresholds go from 0 to n_lines_adjustable. - size_t idx_line_begin = 0; - size_t idx_line_end = 0; -}; - // Calculate a new feedrate when slowing down by time_stretch for segments faster than min_feedrate. // Used by non-proportional slow down. float new_feedrate_to_reach_time_stretch( @@ -294,7 +98,77 @@ finished: return new_feedrate; } -std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, bool flush) +static void exclude_participate_in_speed_slowdown(std::vector> &lines, std::vector &per_extruder_adjustments, CoolingNode &node, bool is_outwall) +{ + for (std::pair line_pos : lines) { + CoolingLine &line = per_extruder_adjustments[line_pos.second].lines[line_pos.first]; + if (line.feedrate > node.filter_feedrate) { + if(is_outwall) + line.feedrate = node.filter_feedrate; + else + line.feedrate *= node.rate; + } + + // not adjust outwal line speed + line.type = line.type & (~CoolingLine::TYPE_ADJUSTABLE); + // update time cost + if (line.feedrate == 0 || line.length == 0) + line.time = 0; + else + line.time = line.length / line.feedrate; + } +} + +//BBS: apply the smooth outwall speed and recalculate the layer time +void CoolingBuffer::apply_smooth_speed(SmoothCalculator &smooth_calculator, std::vector &extruder_adjustments, const int layer_id) +{ + //rewrite feedrate + for (size_t obj_id = 0; obj_id < smooth_calculator.layers_wall_collection[layer_id].size(); ++obj_id) { + for (size_t node_id = 0; node_id < smooth_calculator.layers_wall_collection[layer_id][obj_id].cooling_nodes.size(); ++node_id) { + CoolingNode &node = smooth_calculator.layers_wall_collection[layer_id][obj_id].cooling_nodes[node_id]; + node.rate = node.filter_feedrate / node.max_feedrate; + //set outwall speed + exclude_participate_in_speed_slowdown(node.outwall_line, extruder_adjustments, node, true); + + //set inner wall speed + exclude_participate_in_speed_slowdown(node.innerwall_line, extruder_adjustments, node, false); + } + } + + //recalculate cooling of other paths + smooth_calculator.layers_cooling_time[layer_id] = this->calculate_layer_slowdown(extruder_adjustments); + +} +//BBS: apply new layer time and rewite the gcode feedrate +std::string CoolingBuffer::apply_smooth_layer_time(std::string && gcode, + SmoothCalculator & smooth_calculator, + std::vector &extruder_adjustments, + const int layer_id, + const bool smooth_layer_time) +{ + if (smooth_layer_time) { + // update layer time + for (PerExtruderAdjustments &adj : extruder_adjustments) + if (adj.slow_down_layer_time < smooth_calculator.layers_cooling_time[layer_id]) + adj.slow_down_layer_time = smooth_calculator.layers_cooling_time[layer_id]; + + // recalculate feedrate + smooth_calculator.layers_cooling_time[layer_id] = this->calculate_layer_slowdown(extruder_adjustments); + } + // apply cooling + std::string out; + out = this->apply_layer_cooldown(gcode, layer_id, smooth_calculator.layers_cooling_time[layer_id], extruder_adjustments); + + return out; +} + +std::string CoolingBuffer::process_layer(std::string && gcode, + const size_t layer_id, + SmoothCalculator & smooth_calculator, + std::vector &per_extruder_adjustments, + const std::vector & object_label, + const bool flush, + const bool spiral_vase) { // Cache the input G-code. if (m_gcode.empty()) @@ -306,17 +180,96 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b if (flush) { // This is either an object layer or the very last print layer. Calculate cool down over the collected support layers // and one object layer. - std::vector per_extruder_adjustments = this->parse_layer_gcode(m_gcode, m_current_pos); - float layer_time_stretched = this->calculate_layer_slowdown(per_extruder_adjustments); - out = this->apply_layer_cooldown(m_gcode, layer_id, layer_time_stretched, per_extruder_adjustments); + // initial and arrange node collection seq + if (!spiral_vase && layer_id > 0) + smooth_calculator.init_wall_collection(layer_id, object_label); + + //record parse gcode info + per_extruder_adjustments = this->parse_layer_gcode(m_gcode, m_current_pos, object_label, spiral_vase, layer_id > 0); + + //adjustments will be sorted on calculate layer slowdown + smooth_calculator.layers_cooling_time[layer_id] = this->calculate_layer_slowdown(per_extruder_adjustments); + if (spiral_vase || layer_id == 0) { + out = this->apply_layer_cooldown(m_gcode, layer_id, smooth_calculator.layers_cooling_time[layer_id], per_extruder_adjustments); + } else if (layer_id > 0) { + //BBS: update outwall feedrate + // update feedrate of outwall after initial cooling process + + for (size_t extruder_idx = 0; extruder_idx < per_extruder_adjustments.size(); ++extruder_idx) { + PerExtruderAdjustments &extruder_adjustments = per_extruder_adjustments[extruder_idx]; + for (size_t line_idx = 0; line_idx < extruder_adjustments.lines.size(); ++line_idx) { + CoolingLine &line = extruder_adjustments.lines[line_idx]; + if (line.outwall_smooth_mark) { + line.slowdown = true; + + // search node id + if (smooth_calculator.layers_wall_collection[layer_id][line.object_id].cooling_nodes.count(line.cooling_node_id) < 0) { + CoolingNode node; + smooth_calculator.layers_wall_collection[layer_id][line.object_id].cooling_nodes.emplace(line.cooling_node_id, node); + } + + CoolingNode &node = smooth_calculator.layers_wall_collection[layer_id][line.object_id].cooling_nodes[line.cooling_node_id]; + if( line.type & CoolingLine::TYPE_EXTERNAL_PERIMETER ) { + node.outwall_line.emplace_back(line_idx, extruder_idx); + if (node.max_feedrate < line.feedrate) { + node.max_feedrate = line.feedrate; + node.filter_feedrate = node.max_feedrate; + } + } else { + node.innerwall_line.emplace_back(line_idx, extruder_idx); + } + + } else { + line.slowdown = false; + line.feedrate = line.origin_feedrate; + // update time cost + if (line.feedrate == 0 || line.length == 0) + line.time = 0; + else + line.time = line.length / line.feedrate; + line.time_max = line.origin_time_max; + } + } + } + } m_gcode.clear(); } return out; } +static void mark_node_pos(bool &flag, int &line_idx, std::pair &node_pos, const std::vector &object_label, int cooling_node_id, int object_id, PerExtruderAdjustments* adjustment) +{ + for (size_t object_idx = 0; object_idx < object_label.size(); ++object_idx) { + if (object_label[object_idx] == object_id) { + if (cooling_node_id == -1) break; + line_idx = adjustment->lines.size(); + flag = true; + node_pos.first = object_idx; + node_pos.second = cooling_node_id; + break; + } + } +} + +static void record_wall_lines(bool &flag, int &line_idx, PerExtruderAdjustments *adjustment, const std::pair &node_pos) +{ + if (flag && line_idx < adjustment->lines.size()) { + CoolingLine &ptr = adjustment->lines[line_idx]; + ptr.outwall_smooth_mark = true; + ptr.object_id = node_pos.first; + ptr.cooling_node_id = node_pos.second; + flag = false; + } +} + +//native-resource://sandbox_fs/webcontent/resource/assets/img/41ecc25c56.png // Parse the layer G-code for the moves, which could be adjusted. // Return the list of parsed lines, bucketed by an extruder. -std::vector CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const +std::vector CoolingBuffer::parse_layer_gcode(const std::string & gcode, + std::vector & current_pos, + const std::vector & object_label, + bool spiral_vase, + bool join_z_smooth) const { std::vector per_extruder_adjustments(m_extruder_ids.size()); std::vector map_extruder_to_per_extruder_adjustment(m_num_extruders, 0); @@ -337,7 +290,15 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command // for a sequence of extrusion moves. size_t active_speed_modifier = size_t(-1); + int object_id = -1; + int cooling_node_id = -1; + std::string object_id_string = "; OBJECT_ID: "; + std::string cooling_node_label = "; COOLING_NODE: "; + bool append_wall_ptr = false; + bool append_inner_wall_ptr = false; + std::pair node_pos; + int line_idx = -1; for (; *line_start != 0; line_start = line_end) { while (*line_end != '\n' && *line_end != 0) @@ -358,6 +319,15 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.type = CoolingLine::TYPE_G2; else if (boost::starts_with(sline, "G3 ")) line.type = CoolingLine::TYPE_G3; + //BBS: parse object id & node id + else if (boost::starts_with(sline, object_id_string)) { + std::string sub = sline.substr(object_id_string.size()); + object_id = std::stoi(sub); + } else if (boost::starts_with(sline, cooling_node_label)) { + std::string sub = sline.substr(cooling_node_label.size()); + cooling_node_id = std::stoi(sub); + } + if (line.type) { // G0, G1 or G92 // Parse the G-code line. @@ -392,14 +362,32 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER"); bool wipe = boost::contains(sline, ";_WIPE"); - if (external_perimeter) - line.type |= CoolingLine::TYPE_EXTERNAL_PERIMETER; + bool inner_perimeter = boost::contains(sline,";_PERIMETER"); + + record_wall_lines(append_inner_wall_ptr, line_idx, adjustment, node_pos); + if (wipe) line.type |= CoolingLine::TYPE_WIPE; if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) { line.type |= CoolingLine::TYPE_ADJUSTABLE; active_speed_modifier = adjustment->lines.size(); + + if (inner_perimeter && join_z_smooth && !spiral_vase) { + // BBS: collect innerwall info + mark_node_pos(append_inner_wall_ptr, line_idx, node_pos, object_label, cooling_node_id, object_id, adjustment); + } } + + record_wall_lines(append_wall_ptr, line_idx, adjustment, node_pos); + + if (external_perimeter) { + line.type |= CoolingLine::TYPE_EXTERNAL_PERIMETER; + if (line.type & CoolingLine::TYPE_ADJUSTABLE && join_z_smooth && !spiral_vase) { + // BBS: collect outwall info + mark_node_pos(append_wall_ptr, line_idx, node_pos, object_label, cooling_node_id, object_id, adjustment); + } + } + if ((line.type & CoolingLine::TYPE_G92) == 0) { //BBS: G0, G1, G2, G3. Calculate the duration. if (m_config.use_relative_e_distances.value) @@ -429,12 +417,19 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.length = std::abs(dif[3]); } line.feedrate = new_pos[4]; + line.origin_feedrate = new_pos[4]; + assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f); if (line.length > 0) line.time = line.length / line.feedrate; + + if (line.feedrate == 0) + line.time = 0; + line.time_max = line.time; if ((line.type & CoolingLine::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1)) line.time_max = (adjustment->slow_down_min_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->slow_down_min_speed); + line.origin_time_max = line.time_max; // BBS: add G2 and G3 support if (active_speed_modifier < adjustment->lines.size() && ((line.type & CoolingLine::TYPE_G1) || (line.type & CoolingLine::TYPE_G2) || @@ -450,6 +445,8 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: sm.time_max = FLT_MAX; else sm.time_max += line.time_max; + + sm.origin_time_max = sm.time_max; } // Don't store this line. line.type = 0; @@ -489,6 +486,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.time = line.time_max = float( (pos_S > 0) ? atof(sline.c_str() + pos_S + 1) : (pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.); + line.origin_time_max = line.time_max; } else if (boost::starts_with(sline, ";_FORCE_RESUME_FAN_SPEED")) { line.type = CoolingLine::TYPE_FORCE_RESUME_FAN; } else if (boost::starts_with(sline, ";_SET_FAN_SPEED_CHANGING_LAYER")) { diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 90c60e5b1..9d35c574d 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -4,13 +4,215 @@ #include "../libslic3r.h" #include #include +#include namespace Slic3r { class GCode; class Layer; -struct PerExtruderAdjustments; +struct CoolingLine +{ + enum Type { + TYPE_SET_TOOL = 1 << 0, + TYPE_EXTRUDE_END = 1 << 1, + TYPE_OVERHANG_FAN_START = 1 << 2, + TYPE_OVERHANG_FAN_END = 1 << 3, + TYPE_G0 = 1 << 4, + TYPE_G1 = 1 << 5, + TYPE_ADJUSTABLE = 1 << 6, + TYPE_EXTERNAL_PERIMETER = 1 << 7, + // The line sets a feedrate. + TYPE_HAS_F = 1 << 8, + TYPE_WIPE = 1 << 9, + TYPE_G4 = 1 << 10, + TYPE_G92 = 1 << 11, + // BBS: add G2 G3 type + TYPE_G2 = 1 << 12, + TYPE_G3 = 1 << 13, + TYPE_FORCE_RESUME_FAN = 1 << 14, + TYPE_SET_FAN_CHANGING_LAYER = 1 << 15, + }; + + CoolingLine(unsigned int type, size_t line_start, size_t line_end) + : type(type), line_start(line_start), line_end(line_end), length(0.f), feedrate(0.f), origin_feedrate(0.f), time(0.f), time_max(0.f), slowdown(false) + {} + + bool adjustable(bool slowdown_external_perimeters) const + { + return (this->type & TYPE_ADJUSTABLE) && (!(this->type & TYPE_EXTERNAL_PERIMETER) || slowdown_external_perimeters) && this->time < this->time_max; + } + + bool adjustable() const { return (this->type & TYPE_ADJUSTABLE) && this->time < this->time_max; } + + size_t type; + // Start of this line at the G-code snippet. + size_t line_start; + // End of this line at the G-code snippet. + size_t line_end; + // XY Euclidian length of this segment. + float length; + // Current feedrate, possibly adjusted. + float feedrate; + // Current duration of this segment. + float time; + // Maximum duration of this segment. + float time_max; + // If marked with the "slowdown" flag, the line has been slowed down. + bool slowdown; + // Current feedrate, possibly adjusted. + float origin_feedrate = 0; + float origin_time_max = 0; + // Current duration of this segment. + //float origin_time; + bool outwall_smooth_mark = false; + int object_id = -1; + int cooling_node_id = -1; +}; + +struct PerExtruderAdjustments +{ + // Calculate the total elapsed time per this extruder, adjusted for the slowdown. + float elapsed_time_total() const + { + float time_total = 0.f; + for (const CoolingLine &line : lines) time_total += line.time; + return time_total; + } + // Calculate the total elapsed time when slowing down + // to the minimum extrusion feed rate defined for the current material. + float maximum_time_after_slowdown(bool slowdown_external_perimeters) const + { + float time_total = 0.f; + for (const CoolingLine &line : lines) + if (line.adjustable(slowdown_external_perimeters)) { + if (line.time_max == FLT_MAX) + return FLT_MAX; + else + time_total += line.time_max; + } else + time_total += line.time; + return time_total; + } + // Calculate the adjustable part of the total time. + float adjustable_time(bool slowdown_external_perimeters) const + { + float time_total = 0.f; + for (const CoolingLine &line : lines) + if (line.adjustable(slowdown_external_perimeters)) time_total += line.time; + return time_total; + } + // Calculate the non-adjustable part of the total time. + float non_adjustable_time(bool slowdown_external_perimeters) const + { + float time_total = 0.f; + for (const CoolingLine &line : lines) + if (!line.adjustable(slowdown_external_perimeters)) time_total += line.time; + return time_total; + } + // Slow down the adjustable extrusions to the minimum feedrate allowed for the current extruder material. + // Used by both proportional and non-proportional slow down. + float slowdown_to_minimum_feedrate(bool slowdown_external_perimeters) + { + float time_total = 0.f; + for (CoolingLine &line : lines) { + if (line.adjustable(slowdown_external_perimeters)) { + assert(line.time_max >= 0.f && line.time_max < FLT_MAX); + line.slowdown = true; + line.time = line.time_max; + line.feedrate = line.length / line.time; + } + time_total += line.time; + } + return time_total; + } + // Slow down each adjustable G-code line proportionally by a factor. + // Used by the proportional slow down. + float slow_down_proportional(float factor, bool slowdown_external_perimeters) + { + assert(factor >= 1.f); + float time_total = 0.f; + for (CoolingLine &line : lines) { + if (line.adjustable(slowdown_external_perimeters)) { + line.slowdown = true; + line.time = std::min(line.time_max, line.time * factor); + line.feedrate = line.length / line.time; + } + time_total += line.time; + } + return time_total; + } + + // Sort the lines, adjustable first, higher feedrate first. + // Used by non-proportional slow down. + void sort_lines_by_decreasing_feedrate() + { + std::sort(lines.begin(), lines.end(), [](const CoolingLine &l1, const CoolingLine &l2) { + bool adj1 = l1.adjustable(); + bool adj2 = l2.adjustable(); + return (adj1 == adj2) ? l1.feedrate > l2.feedrate : adj1; + }); + for (n_lines_adjustable = 0; n_lines_adjustable < lines.size() && this->lines[n_lines_adjustable].adjustable(); ++n_lines_adjustable) + ; + time_non_adjustable = 0.f; + for (size_t i = n_lines_adjustable; i < lines.size(); ++i) time_non_adjustable += lines[i].time; + } + + // Calculate the maximum time stretch when slowing down to min_feedrate. + // Slowdown to min_feedrate shall be allowed for this extruder's material. + // Used by non-proportional slow down. + float time_stretch_when_slowing_down_to_feedrate(float min_feedrate) const + { + float time_stretch = 0.f; + assert(this->slow_down_min_speed < min_feedrate + EPSILON); + for (size_t i = 0; i < n_lines_adjustable; ++i) { + const CoolingLine &line = lines[i]; + if (line.feedrate > min_feedrate) time_stretch += line.time * (line.feedrate / min_feedrate - 1.f); + } + return time_stretch; + } + + // Slow down all adjustable lines down to min_feedrate. + // Slowdown to min_feedrate shall be allowed for this extruder's material. + // Used by non-proportional slow down. + void slow_down_to_feedrate(float min_feedrate) + { + assert(this->slow_down_min_speed < min_feedrate + EPSILON); + for (size_t i = 0; i < n_lines_adjustable; ++i) { + CoolingLine &line = lines[i]; + if (line.feedrate > min_feedrate) { + line.time *= std::max(1.f, line.feedrate / min_feedrate); + line.feedrate = min_feedrate; + line.slowdown = true; + } + } + } + + // Extruder, for which the G-code will be adjusted. + unsigned int extruder_id = 0; + // Is the cooling slow down logic enabled for this extruder's material? + bool cooling_slow_down_enabled = false; + // Slow down the print down to slow_down_min_speed if the total layer time is below slow_down_layer_time. + float slow_down_layer_time = 0.f; + // Minimum print speed allowed for this extruder. + float slow_down_min_speed = 0.f; + + // Parsed lines. + std::vector lines; + // The following two values are set by sort_lines_by_decreasing_feedrate(): + // Number of adjustable lines, at the start of lines. + size_t n_lines_adjustable = 0; + // Non-adjustable time of lines starting with n_lines_adjustable. + float time_non_adjustable = 0; + // Current total time for this extruder. + float time_total = 0; + // Maximum time for this extruder, when the maximum slow down is applied. + float time_maximum = 0; + + // Temporaries for processing the slow down. Both thresholds go from 0 to n_lines_adjustable. + size_t idx_line_begin = 0; + size_t idx_line_end = 0; +}; // A standalone G-code filter, to control cooling of the print. // The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited // and the print is modified to stretch over a minimum layer time. @@ -25,16 +227,29 @@ public: CoolingBuffer(GCode &gcodegen); void reset(const Vec3d &position); void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; } - std::string process_layer(std::string &&gcode, size_t layer_id, bool flush); + std::string process_layer(std::string && gcode, + const size_t layer_id, + SmoothCalculator & smooth_calculator, + std::vector &per_extruder_adjustments, + const std::vector & object_label, + const bool flush, + const bool spiral_vase); + + //BBS: + void apply_smooth_speed(SmoothCalculator &smooth_calculator, std::vector &extruder_adjustments, const int layer_id); + std::string apply_smooth_layer_time(std::string &&gcode, SmoothCalculator &smooth_calculator, std::vector &extruder_adjustments, const int layer_id, const bool smooth_layer_time); private: CoolingBuffer& operator=(const CoolingBuffer&) = delete; - std::vector parse_layer_gcode(const std::string &gcode, std::vector ¤t_pos) const; + std::vector parse_layer_gcode(const std::string & gcode, + std::vector & current_pos, + const std::vector & object_label, + bool spiral_vase, + bool join_z_smooth) const; float calculate_layer_slowdown(std::vector &per_extruder_adjustments); // Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed. // Returns the adjusted G-code. std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector &per_extruder_adjustments); - // G-code snippet cached for the support layers preceding an object layer. std::string m_gcode; // Internal data. diff --git a/src/libslic3r/GCode/Smoothing.hpp b/src/libslic3r/GCode/Smoothing.hpp new file mode 100644 index 000000000..96e9997bb --- /dev/null +++ b/src/libslic3r/GCode/Smoothing.hpp @@ -0,0 +1,219 @@ +#ifndef slic3r_Smoothing_hpp_ +#define slic3r_Smoothing_hpp_ +#include "../libslic3r.h" + +#include + +namespace Slic3r { + +static const int guassian_window_size = 11; +static const int guassian_r = 2; +static const int guassian_stop_threshold = 5; +static const float guassian_layer_time_stop_threshold = 3.0; +// if the layer time longer than this threshold, ignore it +static const float layer_time_ignore_threshold = 30.0; +static const int max_steps_count = 1000; + +struct CoolingNode +{ + // extruder pos, line pos; + std::vector> outwall_line; + std::vector> innerwall_line; + float max_feedrate = 0; + float filter_feedrate = 0; + double rate = 1; +}; + +struct OutwallCollection +{ + int object_id; + std::map cooling_nodes; +}; + +class SmoothCalculator +{ + std::vector guassian_filter; + double filter_sum = .0f; + +public: + std::vector> layers_wall_collection; + std::vector layers_cooling_time; + std::vector>> objects_node_range; + + SmoothCalculator(const int print_size, const int objects_size) + { + guassian_filter_generator(); + objects_node_range.resize(objects_size); + layers_wall_collection.resize(print_size); + layers_cooling_time.resize(print_size); + } + + // guassian filter + double guassian_function(double x, double r) { + return exp(-x * x / (2 * r * r)) / (r * sqrt(2 * PI)); + } + + void guassian_filter_generator() { + double r = guassian_r; + int half_win_size = guassian_window_size / 2; + for (int start = -half_win_size; start <= half_win_size; ++start) { + double y = guassian_function(start, r); + filter_sum += y; + guassian_filter.push_back(y); + } + } + + void init_wall_collection(const int layer_id, const std::vector &object_label) + { + for (size_t object_idx = 0; object_idx < object_label.size(); ++object_idx) { + OutwallCollection object_level; + object_level.object_id = object_label[object_idx]; + layers_wall_collection[layer_id].push_back(object_level); + } + } + + void init_object_node_range() { + for (size_t object_id = 0; object_id < objects_node_range.size(); ++object_id) { + + for (size_t layer_id = 1; layer_id < layers_wall_collection.size(); ++layer_id) { + const OutwallCollection &each_object = layers_wall_collection[layer_id][object_id]; + auto it = each_object.cooling_nodes.begin(); + while (it != each_object.cooling_nodes.end()) { + if (objects_node_range[object_id].count(it->first) == 0) { + objects_node_range[object_id].emplace(it->first, std::pair(layer_id, layer_id)); + } else { + objects_node_range[object_id][it->first].second = layer_id; + } + it++; + } + } + } + } + + // filter the data + void layer_speed_filter(const int object_id, const int node_id) + { + int start_pos = guassian_filter.size() / 2; + // first layer don't need to be smoothed + int layer_id = objects_node_range[object_id][node_id].first; + int layer_end = objects_node_range[object_id][node_id].second; + + for (; layer_id <= layer_end; ++layer_id) { + if (layers_wall_collection[layer_id][object_id].cooling_nodes.count(node_id) == 0) + break; + + CoolingNode &node = layers_wall_collection[layer_id][object_id].cooling_nodes[node_id]; + + if (node.outwall_line.empty()) + continue; + + double conv_sum = 0; + for (int filter_pos_idx = 0; filter_pos_idx < guassian_filter.size(); ++filter_pos_idx) { + int remap_data_pos = layer_id - start_pos + filter_pos_idx; + + if (remap_data_pos < 1) + remap_data_pos = 1; + else if (remap_data_pos > layers_wall_collection.size() - 1) + remap_data_pos = layers_wall_collection.size() - 1; + + // some node may not start at layer 1 + double remap_data = node.filter_feedrate; + if (!layers_wall_collection[remap_data_pos][object_id].cooling_nodes[node_id].outwall_line.empty()) + remap_data = layers_wall_collection[remap_data_pos][object_id].cooling_nodes[node_id].filter_feedrate; + + conv_sum += guassian_filter[filter_pos_idx] * remap_data; + } + double filter_res = conv_sum / filter_sum; + if (filter_res < node.filter_feedrate) node.filter_feedrate = filter_res; + } + } + + bool speed_filter_continue(const int object_id, const int node_id) + { + int layer_id = objects_node_range[object_id][node_id].first; + int layer_end = objects_node_range[object_id][node_id].second; + + for (; layer_id < layer_end; ++layer_id) { + if (std::abs(layers_wall_collection[layer_id][object_id].cooling_nodes[node_id].outwall_line.empty())) + continue; + + if (std::abs(layers_wall_collection[layer_id][object_id].cooling_nodes[node_id].filter_feedrate - + layers_wall_collection[layer_id + 1][object_id].cooling_nodes[node_id].filter_feedrate) > + guassian_stop_threshold) + return true; + } + return false; + } + + void smooth_layer_speed() + { + init_object_node_range(); + + for (size_t obj_id = 0; obj_id < objects_node_range.size(); ++obj_id) { + auto it = objects_node_range[obj_id].begin(); + while (it != objects_node_range[obj_id].end()) { + int step_count = 0; + while (step_count < max_steps_count && speed_filter_continue(obj_id, it->first)) { + step_count++; + layer_speed_filter(obj_id, it->first); + } + it++; + } + } + } + + // filter the data + void filter_layer_time() + { + int start_pos = guassian_filter.size() / 2; + // first layer don't need to be smoothed + for (int layer_id = 1; layer_id < layers_cooling_time.size(); ++layer_id) { + if (layers_cooling_time[layer_id] > layer_time_ignore_threshold) + continue; + + double conv_sum = 0; + for (int filter_pos_idx = 0; filter_pos_idx < guassian_filter.size(); ++filter_pos_idx) { + int remap_data_pos = layer_id - start_pos + filter_pos_idx; + + if (remap_data_pos < 1) + remap_data_pos = 1; + else if (remap_data_pos > layers_cooling_time.size() - 1) + remap_data_pos = layers_cooling_time.size() - 1; + + // if the layer time big enough, surface defact will disappear + double data_temp = layers_cooling_time[remap_data_pos] > layer_time_ignore_threshold ? layer_time_ignore_threshold : layers_cooling_time[remap_data_pos]; + + conv_sum += guassian_filter[filter_pos_idx] * data_temp; + } + double filter_res = conv_sum / filter_sum; + filter_res = filter_res > layer_time_ignore_threshold ? layer_time_ignore_threshold : filter_res; + if (filter_res > layers_cooling_time[layer_id]) + layers_cooling_time[layer_id] = filter_res; + } + } + + bool layer_time_filter_continue() + { + for (int layer_id = 1; layer_id < layers_cooling_time.size() - 1; ++layer_id) { + double layer_time = layers_cooling_time[layer_id] > layer_time_ignore_threshold ? layer_time_ignore_threshold : layers_cooling_time[layer_id]; + double layer_time_cmp = layers_cooling_time[layer_id + 1] > layer_time_ignore_threshold ? layer_time_ignore_threshold : layers_cooling_time[layer_id + 1]; + + if (std::abs(layer_time - layer_time_cmp) > guassian_layer_time_stop_threshold) + return true; + } + return false; + } + + void smooth_layer_time() + { + int step_count = 0; + while (step_count < max_steps_count && layer_time_filter_continue()) { + step_count++; + filter_layer_time(); + } + } +}; + +} // namespace Slic3r + +#endif \ No newline at end of file diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 026530198..f18afd10e 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -5,8 +5,9 @@ #include "ShortestPath.hpp" #include "SVG.hpp" #include "BoundingBox.hpp" - +#include "libslic3r/AABBTreeLines.hpp" #include +static const int Continuitious_length = scale_(0.01); namespace Slic3r { @@ -198,7 +199,7 @@ void Layer::make_perimeters() if (layerms.size() == 1) { // optimization (*layerm)->fill_surfaces.surfaces.clear(); - (*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons); + (*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons, region_id, this->loop_nodes); (*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces); } else { SurfaceCollection new_slices; @@ -222,7 +223,7 @@ void Layer::make_perimeters() SurfaceCollection fill_surfaces; //BBS ExPolygons fill_no_overlap; - layerm_config->make_perimeters(new_slices, &fill_surfaces, &fill_no_overlap); + layerm_config->make_perimeters(new_slices, &fill_surfaces, &fill_no_overlap, region_id, this->loop_nodes); // assign fill_surfaces to each layer if (!fill_surfaces.surfaces.empty()) { @@ -237,9 +238,127 @@ void Layer::make_perimeters() } } } + BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done"; } +//BBS: use aabbtree to get distance +class ContinuitiousDistancer +{ + std::vector lines; + AABBTreeIndirect::Tree<2, double> tree; + +public: + ContinuitiousDistancer(const std::pair node_expolygon) + { + if (node_expolygon.second) { + for (const auto &line : node_expolygon.first.lines()) + lines.emplace_back(line.a.cast(), line.b.cast()); + } else { + Polyline pl; + pl.append(node_expolygon.first.contour.points); + for (const auto &line : pl.lines()) + lines.emplace_back(line.a.cast(), line.b.cast()); + } + tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); + } + + float distance_from_perimeter(const Vec2f &point) const + { + Vec2d p = point.cast(); + size_t hit_idx_out{}; + Vec2d hit_point_out = Vec2d::Zero(); + auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); + if (distance < 0) { + return std::numeric_limits::max(); + } + + distance = sqrt(distance); + const Linef &line = lines[hit_idx_out]; + Vec2d v1 = line.b - line.a; + Vec2d v2 = p - line.a; + if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { distance *= -1; } + return distance; + } +}; + +void Layer::calculate_perimeter_continuity(std::vector &prev_nodes) { + for (size_t node_pos = 0; node_pos < loop_nodes.size(); ++node_pos) { + LoopNode &node=loop_nodes[node_pos]; + double width = scale_(m_regions[node.region_id]->region().config().outer_wall_line_width) * 1.2; + ContinuitiousDistancer node_distancer(node.exp); + for (size_t prev_pos = 0; prev_pos < prev_nodes.size(); ++prev_pos) { + LoopNode &prev_node = prev_nodes[prev_pos]; + + // no overlap or has diff speed + if (!node.bbox.overlap(prev_node.bbox)) + continue; + + //calculate dist, checkout the continuity + Polyline continuitious_pl; + //check start pt + size_t start = 0; + bool conntiouitious_flag = false; + size_t end = prev_node.exp.first.contour.points.size() - 1; + + //if the countor is loop + if (prev_node.exp.second) { + //check first point + Point pt = prev_node.exp.first.contour.points.front(); + float dist = node_distancer.distance_from_perimeter(pt.cast()); + if (dist < width && dist > -width) + continuitious_pl.append_before(pt); + + for (; end >= 0; --end) { + if (continuitious_pl.length() >= Continuitious_length) { + node.lower_node_id.push_back(prev_node.node_id); + prev_node.upper_node_id.push_back(node.node_id); + conntiouitious_flag = true; + break; + } + + Point pt = prev_node.exp.first.contour.points[end]; + float dist = node_distancer.distance_from_perimeter(pt.cast()); + + if (dist < width && dist > -width) + continuitious_pl.append_before(pt); + else + break; + } + + if (conntiouitious_flag) continue; + } + + for (; start < end; ++start) { + Point pt = prev_node.exp.first.contour.points[start]; + float dist = node_distancer.distance_from_perimeter(pt.cast()); + + if (dist < width && dist > -width) { + continuitious_pl.append(pt); + continue; + } + + if (continuitious_pl.empty() || continuitious_pl.length() < Continuitious_length) { + continuitious_pl.clear(); + continue; + } + + node.lower_node_id.push_back(prev_node.node_id); + prev_node.upper_node_id.push_back(node.node_id); + continuitious_pl.clear(); + break; + + } + + if (continuitious_pl.length() >= Continuitious_length) { + node.lower_node_id.push_back(prev_node.node_id); + prev_node.upper_node_id.push_back(node.node_id); + } + } + } + +} + void Layer::export_region_slices_to_svg(const char *path) const { BoundingBox bbox; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 811eaf871..ee55fb818 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -80,7 +80,7 @@ public: void slices_to_fill_surfaces_clipped(); void prepare_fill_surfaces(); //BBS - void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap); + void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap, int region_id, std::vector &loop_nodes); void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered); double infill_area_threshold() const; // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer. @@ -155,6 +155,7 @@ public: // BBS ExPolygons loverhangs; BoundingBox loverhangs_bbox; + std::vector loop_nodes; size_t region_count() const { return m_regions.size(); } const LayerRegion* get_region(int idx) const { return m_regions[idx]; } LayerRegion* get_region(int idx) { return m_regions[idx]; } @@ -180,6 +181,9 @@ public: return false; } void make_perimeters(); + //BBS + void calculate_perimeter_continuity(std::vector &prev_nodes); + // Phony version of make_fills() without parameters for Perl integration only. void make_fills() { this->make_fills(nullptr, nullptr); } void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 363e1311c..35d47b9ca 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -64,7 +64,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped() } } -void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap) +void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection *fill_surfaces, ExPolygons *fill_no_overlap, int region_id, std::vector &loop_nodes) { this->perimeters.clear(); this->thin_fills.clear(); @@ -93,7 +93,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec &this->thin_fills, fill_surfaces, //BBS - fill_no_overlap + fill_no_overlap, + region_id, + &loop_nodes ); if (this->layer()->lower_layer != nullptr) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index fdb828e4b..f0cc81997 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -7,6 +7,7 @@ #include "Clipper2Utils.hpp" #include "Arachne/WallToolPaths.hpp" #include "Line.hpp" +#include "Layer.hpp" #include #include #include @@ -1162,6 +1163,7 @@ void PerimeterGenerator::process_classic() ExPolygons gaps; ExPolygons top_fills; ExPolygons fill_clip; + std::vector> outwall; if (loop_number >= 0) { // In case no perimeters are to be generated, loop_number will equal to -1. std::vector contours(loop_number+1); // depth => loops @@ -1287,6 +1289,23 @@ void PerimeterGenerator::process_classic() //BBS: save perimeter loop which use smaller width if (i == 0) { + //store outer wall + + //not loop + for (const ThickPolyline &polyline : thin_walls) { + ExPolygon contour_coll; + contour_coll.contour.append(polyline.points); + outwall.emplace_back(contour_coll, false); + } + + //loop + for (const ExPolygon &expolygon : offsets_with_smaller_width) { + outwall.emplace_back(expolygon, true); + } + for (const ExPolygon &expolygon : offsets) { + outwall.emplace_back(expolygon, true); + } + for (const ExPolygon& expolygon : offsets_with_smaller_width) { contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, true)); if (!expolygon.holes.empty()) { @@ -1463,6 +1482,41 @@ void PerimeterGenerator::process_classic() } entities.entities = std::move( entities_reorder); } + + //BBS: add node for loops + if (!outwall.empty() && this->layer_id > 0) { + entities.loop_node_range.first = this->loop_nodes->size(); + if (outwall.size() == 1) { + LoopNode node; + node.node_id = this->loop_nodes->size(); + node.region_id = region_id; + node.perimeter_id = this->loops->entities.size(); + node.exp = outwall.front(); + node.bbox = get_extents(node.exp.first); + this->loop_nodes->push_back(node); + } else { + int entity_idx = 0; + for (std::pair &exp : outwall) { + LoopNode node; + node.node_id = this->loop_nodes->size(); + node.region_id = region_id; + node.perimeter_id = this->loops->entities.size(); + + for (; entity_idx < entities.entities.size(); ++entity_idx) { + if (exp.first.contains(entities.entities[entity_idx]->first_point())) { + node.loop_id=entity_idx; + node.exp=exp; + node.bbox = get_extents(node.exp.first); + this->loop_nodes->push_back(node); + break; + } + } + } + } + entities.loop_node_range.second = this->loop_nodes->size(); + } + + // append perimeters for this slice as a collection if (! entities.empty()) this->loops->append(entities); diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 709910a5b..c8ac9e1e4 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -40,8 +40,9 @@ public: std::pair m_lower_overhang_dist_boundary; std::pair m_external_overhang_dist_boundary; std::pair m_smaller_external_overhang_dist_boundary; + std::vector *loop_nodes; + int region_id; - PerimeterGenerator( // Input: const SurfaceCollection* slices, @@ -59,14 +60,20 @@ public: // Infills without the gap fills SurfaceCollection* fill_surfaces, //BBS - ExPolygons* fill_no_overlap) + ExPolygons* fill_no_overlap, + int region_id, + std::vector *loop_nodes) : slices(slices), upper_slices(nullptr), lower_slices(nullptr), layer_height(layer_height), layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), solid_infill_flow(flow), config(config), object_config(object_config), print_config(print_config), m_spiral_vase(spiral_mode), - m_scaled_resolution(scaled(print_config->resolution.value > EPSILON ? print_config->resolution.value : EPSILON)), - loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), fill_no_overlap(fill_no_overlap), + m_scaled_resolution(scaled(print_config->resolution.value > EPSILON ? print_config->resolution.value : EPSILON)), loops(loops), + gap_fill(gap_fill), + fill_surfaces(fill_surfaces), + fill_no_overlap(fill_no_overlap), + region_id(region_id), + loop_nodes(loop_nodes), m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1), m_ext_mm3_per_mm_smaller_width(-1) {} diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index e6324e0fe..25fb22616 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -842,7 +842,8 @@ static std::vector s_Preset_print_options { "seam_gap", "wipe_speed", "top_solid_infill_flow_ratio", "initial_layer_flow_ratio", "default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk", "travel_jerk", "filter_out_gap_fill", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", - "small_perimeter_speed", "small_perimeter_threshold", + "small_perimeter_speed", "small_perimeter_threshold", "z_direction_outwall_speed_continuous", + "layer_time_smoothing", // calib "print_flow_ratio", //Orca diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 2d5168e36..30fee08bd 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -505,7 +505,7 @@ private: // BBS SupportNecessaryType is_support_necessary(); - + void merge_layer_node(const size_t layer_id, int &max_merged_id, std::map>> &node_record); // XYZ in scaled coordinates Vec3crd m_size; double m_max_z; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index b35f98836..9c255481b 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -571,6 +571,20 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionBool(false)); + def = this->add("z_direction_outwall_speed_continuous", coBool); + def->label = L("Z direction outwall speed continuous"); + def->category = L("Quality"); + def->tooltip = L("Smoothing outwall speed in z direction to get better surface quality. Print time will increases."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("layer_time_smoothing", coBool); + def->label = L("Layer time smoothing"); + def->category = L("Quality"); + def->tooltip = L("Smoothing layer time in z direction to get better surface quality. Print time will increases."); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("max_travel_detour_distance", coFloatOrPercent); def->label = L("Avoid crossing wall - Max detour length"); def->category = L("Quality"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 7869fb667..79ce09398 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -981,6 +981,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( //BBS ((ConfigOptionInts, additional_cooling_fan_speed)) ((ConfigOptionBool, reduce_crossing_wall)) + ((ConfigOptionBool, z_direction_outwall_speed_continuous)) + ((ConfigOptionBool, layer_time_smoothing)) ((ConfigOptionFloatOrPercent, max_travel_detour_distance)) ((ConfigOptionPoints, printable_area)) //BBS: add bed_exclude_area diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 1755d866d..65c9405a5 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -129,6 +129,63 @@ std::vector> PrintObject::all_regions( return out; } +void PrintObject::merge_layer_node(const size_t layer_id, int &max_merged_id, std::map>> &node_record) +{ + Layer *this_layer = m_layers[layer_id]; + std::vector &loop_nodes = this_layer->loop_nodes; + for (size_t idx = 0; idx < loop_nodes.size(); ++idx) { + //new cool node + if (loop_nodes[idx].lower_node_id.empty()) { + max_merged_id++; + loop_nodes[idx].merged_id = max_merged_id; + std::vector> node_pos; + node_pos.emplace_back(layer_id, idx); + node_record.emplace(max_merged_id, node_pos); + continue; + } + + //it should finds key in map + if (loop_nodes[idx].lower_node_id.size() == 1) { + loop_nodes[idx].merged_id = m_layers[layer_id - 1]->loop_nodes[loop_nodes[idx].lower_node_id.front()].merged_id; + node_record[loop_nodes[idx].merged_id].emplace_back(layer_id, idx); + continue; + } + + //min index + int min_merged_id = -1; + std::vector appear_id; + for (size_t lower_idx = 0; lower_idx < loop_nodes[idx].lower_node_id.size(); ++lower_idx) { + int id = m_layers[layer_id - 1]->loop_nodes[loop_nodes[idx].lower_node_id[lower_idx]].merged_id; + if (min_merged_id == -1 || min_merged_id > id) + min_merged_id = id; + appear_id.push_back(id); + } + + loop_nodes[idx].merged_id = min_merged_id; + node_record[min_merged_id].emplace_back(layer_id, idx); + + //update other node merged id + for (size_t appear_node_idx = 0; appear_node_idx < appear_id.size(); ++appear_node_idx) { + if (appear_id[appear_node_idx] == min_merged_id) + continue; + + auto it = node_record.find(appear_id[appear_node_idx]); + std::vector> &appear_node_pos = it->second; + + for (size_t node_idx = 0; node_idx < appear_node_pos.size(); ++node_idx) { + int node_layer = appear_node_pos[node_idx].first; + int node_pos = appear_node_pos[node_idx].second; + + LoopNode &node = m_layers[node_layer]->loop_nodes[node_pos]; + + node.merged_id = min_merged_id; + node_record[min_merged_id].emplace_back(node_layer, node_pos); + } + node_record.erase(it); + } + } +} + // 1) Merges typed region slices into stInternal type. // 2) Increases an "extra perimeters" counter at region slices where needed. // 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). @@ -236,6 +293,33 @@ void PrintObject::make_perimeters() m_print->throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end"; + // BBS: get continuity of nodes + if (this->config().wall_generator == PerimeterGeneratorType::Classic) { + BOOST_LOG_TRIVIAL(debug) << "Calculating perimeters connection in parallel - start"; + tbb::parallel_for(tbb::blocked_range(0, m_layers.size()), [this](const tbb::blocked_range &range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { + m_print->throw_if_canceled(); + if (layer_idx > 1) { + Layer &prev_layer = *m_layers[layer_idx - 1]; + m_layers[layer_idx]->calculate_perimeter_continuity(m_layers[layer_idx - 1]->loop_nodes); + } + } + }); + + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Calculating perimeters connection in parallel - end"; + + BOOST_LOG_TRIVIAL(debug) << "Calculating cooling nodes - start"; + + int max_merged_id = -1; + std::map>> node_record; + for (size_t layer_idx = 1; layer_idx < m_layers.size(); ++layer_idx) { + m_print->throw_if_canceled(); + merge_layer_node(layer_idx, max_merged_id, node_record); + } + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Calculating cooling nodes - end"; + } this->set_done(posPerimeters); } @@ -917,7 +1001,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "sparse_infill_speed" || opt_key == "inner_wall_speed" || opt_key == "internal_solid_infill_speed" - || opt_key == "top_surface_speed") { + || opt_key == "top_surface_speed" + || opt_key == "z_direction_outwall_speed_continuous" + || opt_key == "layer_time_smoothing") { invalidated |= m_print->invalidate_step(psGCodeExport); } else if ( opt_key == "flush_into_infill" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 2e71ef8bd..5920ffb46 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -720,10 +720,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line(el, has_fuzzy_skin); bool have_arachne = config->opt_enum("wall_generator") == PerimeterGeneratorType::Arachne; - for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "min_feature_size", "min_bead_width", "wall_distribution_count" }) + for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "min_feature_size", "min_bead_width", "wall_distribution_count"}) toggle_line(el, have_arachne); toggle_field("detect_thin_wall", !have_arachne); + toggle_field("z_direction_outwall_speed_continuous", !have_arachne); PresetBundle *preset_bundle = wxGetApp().preset_bundle; // OrcaSlicer diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f7d06fc64..a3a092a13 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1956,6 +1956,8 @@ void TabPrint::build() optgroup->append_single_option_line("smooth_speed_discontinuity_area"); optgroup->append_single_option_line("smooth_coefficient"); optgroup->append_single_option_line("reduce_crossing_wall"); + optgroup->append_single_option_line("z_direction_outwall_speed_continuous"); + optgroup->append_single_option_line("layer_time_smoothing"); optgroup->append_single_option_line("max_travel_detour_distance"); page = add_options_page(L("Strength"), "empty");