diff --git a/resources/profiles/QIDITechnology.ini b/resources/profiles/QIDITechnology.ini index 32cf53c..856277b 100644 --- a/resources/profiles/QIDITechnology.ini +++ b/resources/profiles/QIDITechnology.ini @@ -128,11 +128,13 @@ ironing_type = top layer_height = 0.2 max_bridge_length = 10 max_print_speed = 600 +max_resonance_avoidance_speed = 120 max_volumetric_extrusion_rate_slope_negative = 0 max_volumetric_extrusion_rate_slope_positive = 0 max_volumetric_speed = 0 min_bead_width = 85% min_feature_size = 25% +min_resonance_avoidance_speed = 70 min_skirt_length = 5 mmu_segmented_region_interlocking_depth = 0 mmu_segmented_region_max_width = 0 @@ -160,6 +162,7 @@ raft_first_layer_density = 90% raft_first_layer_expansion = 3 raft_layers = 0 resolution = 0 +resonance_avoidance = 1 seam_gap = 15% seam_position = aligned single_extruder_multi_material_priming = 0 @@ -169,7 +172,7 @@ skirts = 0 slice_closing_radius = 0.049 slicing_mode = regular slow_down_layers = 0 -small_perimeter_speed = 70 +small_perimeter_speed = 100 solid_infill_acceleration = 10000 solid_infill_below_area = 15 solid_infill_every_layers = 0 @@ -2529,9 +2532,7 @@ machine_min_extruding_rate = 0 machine_min_travel_rate = 0 max_layer_height = 0.28 max_print_height = 315 -max_resonance_avoidance_speed = 115 min_layer_height = 0.08 -min_resonance_avoidance_speed = 70 multimaterial_purging = 45 nozzle_diameter = 0.4 parking_pos_retraction = 92 @@ -2546,7 +2547,6 @@ printer_vendor = QIDI printhost_apikey = printhost_cafile = remaining_times = 1 -resonance_avoidance = 1 retract_before_travel = 1 retract_before_wipe = 0% retract_layer_change = 1 diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a59279d..050e187 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -83,7 +83,6 @@ namespace Slic3r { gcode += '\n'; } - // Return true if tch_prefix is found in custom_gcode static bool custom_gcode_changes_tool(const std::string& custom_gcode, const std::string& tch_prefix, unsigned next_extruder) { @@ -146,13 +145,14 @@ namespace Slic3r { int OozePrevention::_get_temp(const GCodeGenerator &gcodegen) const { + // First layer temperature should be used when on the first layer (obviously) and when + // "other layers" is set to zero (which means it should not be used). return (gcodegen.layer() == nullptr || gcodegen.layer()->id() == 0 || gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()) == 0) ? gcodegen.config().first_layer_temperature.get_at(gcodegen.writer().extruder()->id()) : gcodegen.config().temperature.get_at(gcodegen.writer().extruder()->id()); } - const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.extruder()->id()) @@ -162,7 +162,7 @@ void GCodeGenerator::PlaceholderParserIntegration::reset() this->failed_templates.clear(); this->output_config.clear(); this->opt_position = nullptr; - this->opt_zhop = nullptr; + this->opt_zhop = nullptr; this->opt_e_position = nullptr; this->opt_e_retracted = nullptr; this->opt_e_restart_extra = nullptr; @@ -208,6 +208,7 @@ void GCodeGenerator::PlaceholderParserIntegration::init(const GCodeWriter &write this->position.assign(3, 0); this->opt_position = new ConfigOptionFloats(this->position); this->output_config.set_key_value("position", this->opt_position); + // Store zhop variable into the parser itself, it is a read-only variable to the script. this->opt_zhop = new ConfigOptionFloat(writer.get_zhop()); this->parser.set("zhop", this->opt_zhop); @@ -230,6 +231,7 @@ void GCodeGenerator::PlaceholderParserIntegration::update_from_gcodewriter(const for (const Extruder &e : extruders) { this->e_retracted[e.id()] = e.retracted(); this->e_restart_extra[e.id()] = e.restart_extra(); + // Wipe tower filament consumption has to be added separately, because that gcode is not generated by GCodeWriter. double wt_vol = 0.; const std::vector>>& wtuf = wipe_tower_data.used_filament_until_layer; @@ -552,8 +554,11 @@ GCodeGenerator::GCodeGenerator(const Print* print) : m_brim_done(false), m_second_layer_things_done(false), m_silent_time_estimator_enabled(false), +//Y27 + m_resonance_avoidance(true), m_print(print) {} + void GCodeGenerator::do_export(Print* print, const char* path, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb) { CNumericLocalesSetter locales_setter; @@ -727,75 +732,76 @@ namespace DoExport { // Fill in print_statistics and return formatted string containing filament statistics to be inserted into G-code comment section. static std::string update_print_stats_and_format_filament_stats( const bool has_wipe_tower, - const WipeTowerData &wipe_tower_data, - const FullPrintConfig &config, - const std::vector &extruders, + const WipeTowerData &wipe_tower_data, + const FullPrintConfig &config, + const std::vector &extruders, unsigned int initial_extruder_id, int total_toolchanges, PrintStatistics &print_statistics, bool export_binary_data, bgcode::binarize::BinaryData &binary_data) { - std::string filament_stats_string_out; + std::string filament_stats_string_out; - print_statistics.clear(); + print_statistics.clear(); print_statistics.total_toolchanges = total_toolchanges; print_statistics.initial_extruder_id = initial_extruder_id; std::vector filament_types; - if (! extruders.empty()) { + if (! extruders.empty()) { std::pair out_filament_used_mm(PrintStatistics::FilamentUsedMmMask + " ", 0); std::pair out_filament_used_cm3(PrintStatistics::FilamentUsedCm3Mask + " ", 0); std::pair out_filament_used_g(PrintStatistics::FilamentUsedGMask + " ", 0); std::pair out_filament_cost(PrintStatistics::FilamentCostMask + " ", 0); - for (const Extruder &extruder : extruders) { + for (const Extruder &extruder : extruders) { print_statistics.printing_extruders.emplace_back(extruder.id()); filament_types.emplace_back(config.filament_type.get_at(extruder.id())); double used_filament = extruder.used_filament() + (has_wipe_tower ? wipe_tower_data.used_filament_until_layer.back().second[extruder.id()] : 0.f); double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? wipe_tower_data.used_filament_until_layer.back().second[extruder.id()] * extruder.filament_crossection() : 0.f); // assumes 1.75mm filament diameter - double filament_weight = extruded_volume * extruder.filament_density() * 0.001; - double filament_cost = filament_weight * extruder.filament_cost() * 0.001; + double filament_weight = extruded_volume * extruder.filament_density() * 0.001; + double filament_cost = filament_weight * extruder.filament_cost() * 0.001; auto append = [&extruder](std::pair &dst, const char *tmpl, double value) { assert(is_decimal_separator_point()); - while (dst.second < extruder.id()) { - // Fill in the non-printing extruders with zeros. - dst.first += (dst.second > 0) ? ", 0" : "0"; - ++ dst.second; - } - if (dst.second > 0) - dst.first += ", "; - char buf[64]; - sprintf(buf, tmpl, value); - dst.first += buf; - ++ dst.second; - }; + while (dst.second < extruder.id()) { + // Fill in the non-printing extruders with zeros. + dst.first += (dst.second > 0) ? ", 0" : "0"; + ++ dst.second; + } + if (dst.second > 0) + dst.first += ", "; + char buf[64]; + sprintf(buf, tmpl, value); + dst.first += buf; + ++ dst.second; + }; if (!export_binary_data) { - append(out_filament_used_mm, "%.2lf", used_filament); - append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); + append(out_filament_used_mm, "%.2lf", used_filament); + append(out_filament_used_cm3, "%.2lf", extruded_volume * 0.001); } - if (filament_weight > 0.) { - print_statistics.total_weight = print_statistics.total_weight + filament_weight; + if (filament_weight > 0.) { + print_statistics.total_weight = print_statistics.total_weight + filament_weight; if (!export_binary_data) - append(out_filament_used_g, "%.2lf", filament_weight); - if (filament_cost > 0.) { - print_statistics.total_cost = print_statistics.total_cost + filament_cost; + append(out_filament_used_g, "%.2lf", filament_weight); + if (filament_cost > 0.) { + print_statistics.total_cost = print_statistics.total_cost + filament_cost; if (!export_binary_data) - append(out_filament_cost, "%.2lf", filament_cost); - } - } - print_statistics.total_used_filament += used_filament; - print_statistics.total_extruded_volume += extruded_volume; - print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; + append(out_filament_cost, "%.2lf", filament_cost); + } + } + print_statistics.total_used_filament += used_filament; + print_statistics.total_extruded_volume += extruded_volume; + print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.; print_statistics.total_wipe_tower_filament_weight += has_wipe_tower ? (extruded_volume - extruder.extruded_volume()) * extruder.filament_density() * 0.001 : 0.; - print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; - } + print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.; + } + if (!export_binary_data) { - filament_stats_string_out += out_filament_used_mm.first; - filament_stats_string_out += "\n" + out_filament_used_cm3.first; - if (out_filament_used_g.second) - filament_stats_string_out += "\n" + out_filament_used_g.first; - if (out_filament_cost.second) - filament_stats_string_out += "\n" + out_filament_cost.first; + filament_stats_string_out += out_filament_used_mm.first; + filament_stats_string_out += "\n" + out_filament_used_cm3.first; + if (out_filament_used_g.second) + filament_stats_string_out += "\n" + out_filament_used_g.first; + if (out_filament_cost.second) + filament_stats_string_out += "\n" + out_filament_cost.first; } print_statistics.initial_filament_type = config.filament_type.get_at(initial_extruder_id); std::sort(filament_types.begin(), filament_types.end()); @@ -899,6 +905,7 @@ static inline std::optional find_M84(const std::string &gcode) { return std::nullopt; } + void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb) { const bool export_to_binary_gcode = print.full_print_config().option("binary_gcode")->value; @@ -943,8 +950,8 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail if (it != slicer_metadata.end() && it->first == key) binary_data.printer_metadata.raw_data.emplace_back(*it); } - } - + } + // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); @@ -999,18 +1006,19 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail } if (!export_to_binary_gcode) - // Write information on the generator. - file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); - + // Write information on the generator. + file.write_format("; %s\n\n", Slic3r::header_slic3r_generated().c_str()); if (! export_to_binary_gcode) { // if exporting gcode in ascii format, generate the thumbnails here auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + if (errors != enum_bitmask()) { std::string error_str = format("Invalid thumbnails value:"); error_str += GCodeThumbnails::get_error_string(errors); throw Slic3r::ExportError(error_str); } + if (!thumbnails.empty()) GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails, [&file](const char* sz) { file.write(sz); }, @@ -1038,23 +1046,24 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail assert(! print.config().first_layer_height.percent); const double first_layer_height = print.config().first_layer_height.value; if (!export_to_binary_gcode) { - for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { - const PrintRegion ®ion = print.get_print_region(region_id); - file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); - file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); - file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); - file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); - file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); - if (print.has_support_material()) - file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); - if (print.config().first_layer_extrusion_width.value > 0) - file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); - file.write_format("\n"); + for (size_t region_id = 0; region_id < print.num_print_regions(); ++ region_id) { + const PrintRegion ®ion = print.get_print_region(region_id); + file.write_format("; external perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frExternalPerimeter, layer_height).width()); + file.write_format("; perimeters extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, layer_height).width()); + file.write_format("; infill extrusion width = %.2fmm\n", region.flow(*first_object, frInfill, layer_height).width()); + file.write_format("; solid infill extrusion width = %.2fmm\n", region.flow(*first_object, frSolidInfill, layer_height).width()); + file.write_format("; top infill extrusion width = %.2fmm\n", region.flow(*first_object, frTopSolidInfill, layer_height).width()); + if (print.has_support_material()) + file.write_format("; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width()); + if (print.config().first_layer_extrusion_width.value > 0) + file.write_format("; first layer extrusion width = %.2fmm\n", region.flow(*first_object, frPerimeter, first_layer_height, true).width()); + file.write_format("\n"); + } + print.throw_if_canceled(); } - print.throw_if_canceled(); - } - //B41 + // adds tags for time estimators + //B41 // if (print.config().remaining_times.value) // file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::First_Line_M73_Placeholder).c_str()); @@ -1134,7 +1143,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Label all objects so printer knows about them since the start. m_label_objects.init(print.objects(), print.config().gcode_label_objects, print.config().gcode_flavor); //B41 - // file.write(m_label_objects.all_objects_header()); + //file.write(m_label_objects.all_objects_header()); // Update output variables after the extruders were initialized. m_placeholder_parser_integration.init(m_writer); // Let the start-up script prime the 1st printing tool. @@ -1229,6 +1238,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail } GCode::SmoothPathCache smooth_path_cache_global = smooth_path_interpolate_global(print); + // Do all objects for each layer. if (print.config().complete_objects.value) { size_t finished_objects = 0; @@ -1285,6 +1295,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail m_second_layer_things_done = false; prev_object = &object; } + file.write(m_label_objects.maybe_stop_instance()); } else { // Sort layers by Z. @@ -1293,10 +1304,12 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // QIDI Multi-Material wipe tower. if (has_wipe_tower && ! layers_to_print.empty()) { m_wipe_tower = std::make_unique(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()); + // Set position for wipe tower generation. Vec3d new_position = this->writer().get_position(); new_position.z() = first_layer_height; this->writer().update_position(new_position); + if (print.config().single_extruder_multi_material_priming) { file.write(m_wipe_tower->prime(*this)); // Verify, whether the print overaps the priming extrusions. @@ -1398,7 +1411,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail // Get filament stats. const std::string filament_stats_string_out = DoExport::update_print_stats_and_format_filament_stats( - // Const inputs + // Const inputs has_wipe_tower, print.wipe_tower_data(), this->config(), m_writer.extruders(), @@ -1414,6 +1427,7 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail file.write_format("; objects_info = %s\n", m_label_objects.all_objects_header_singleline_json().c_str()); file.write(filament_stats_string_out); } + if (export_to_binary_gcode) { bgcode::binarize::BinaryData& binary_data = m_processor.get_binary_data(); if (print.m_print_statistics.total_toolchanges > 0) @@ -1421,50 +1435,51 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail char buf[1024]; sprintf(buf, "%.2lf", m_max_layer_z); binary_data.printer_metadata.raw_data.emplace_back("max_layer_z", buf); + // Now the objects info. binary_data.printer_metadata.raw_data.emplace_back("objects_info", m_label_objects.all_objects_header_singleline_json()); } else { // if exporting gcode in ascii format, statistics export is done here - file.write("\n"); + file.write("\n"); file.write_format(PrintStatistics::TotalFilamentUsedGValueMask.c_str(), print.m_print_statistics.total_weight); file.write_format(PrintStatistics::TotalFilamentCostValueMask.c_str(), print.m_print_statistics.total_cost); file.write_format(PrintStatistics::TotalFilamentUsedWipeTowerValueMask.c_str(), print.m_print_statistics.total_wipe_tower_filament_weight); - if (print.m_print_statistics.total_toolchanges > 0) - file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); - file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); + if (print.m_print_statistics.total_toolchanges > 0) + file.write_format("; total toolchanges = %i\n", print.m_print_statistics.total_toolchanges); + file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); - //B3 - if (!export_to_binary_gcode) { - // if exporting gcode in ascii format, generate the thumbnails here - auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); - if (errors != enum_bitmask()) { - std::string error_str = format("Invalid thumbnails value:"); - error_str += GCodeThumbnails::get_error_string(errors); - throw Slic3r::ExportError(error_str); + //B3 + if (!export_to_binary_gcode) { + auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config()); + if (errors != enum_bitmask()) { + std::string error_str = format("Invalid thumbnails value:"); + error_str += GCodeThumbnails::get_error_string(errors); + throw Slic3r::ExportError(error_str); + } + if (!thumbnails.empty()) + GCodeThumbnails::export_qidi_thumbnails_to_file( + thumbnail_cb, thumbnails, [&file](const char *sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); } - if (!thumbnails.empty()) - GCodeThumbnails::export_qidi_thumbnails_to_file( - thumbnail_cb, thumbnails, [&file](const char *sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); - } - file.write("\n"); + file.write("\n"); - // Append full config, delimited by two 'phony' configuration keys qidislicer_config = begin and qidislicer_config = end. - // The delimiters are structured as configuration key / value pairs to be parsable by older versions of QIDISlicer G-code viewer. - { - file.write("\n; qidislicer_config = begin\n"); - std::string full_config; + // if exporting gcode in ascii format, config export is done here + // Append full config, delimited by two 'phony' configuration keys qidislicer_config = begin and qidislicer_config = end. + // The delimiters are structured as configuration key / value pairs to be parsable by older versions of QIDISlicer G-code viewer. + { + file.write("\n; qidislicer_config = begin\n"); + std::string full_config; append_full_config(*m_print, full_config); - if (!full_config.empty()) - file.write(full_config); + if (!full_config.empty()) + file.write(full_config); file.write("; qidislicer_config = end\n"); } if (std::optional line_M84 = find_M84(print.config().end_gcode); is_mk2_or_mk3(print.config().printer_model) && line_M84.has_value()) { file.writeln(*line_M84); - } + } } print.throw_if_canceled(); } @@ -1485,6 +1500,7 @@ void GCodeGenerator::smooth_path_interpolate( if (const SupportLayer *layer = object_layer_to_print.support_layer; layer) out.interpolate_add(layer->support_fills, params); } + // 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 // and export G-code into file. @@ -1542,7 +1558,6 @@ void GCodeGenerator::process_layers( [spiral_vase = this->m_spiral_vase.get(), &layers_to_print](LayerResult in) -> LayerResult { if (in.nop_layer_result) return in; - spiral_vase->enable(in.spiral_vase_enable); bool last_layer = in.layer_id == layers_to_print.size() - 1; return { spiral_vase->process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush}; @@ -1575,10 +1590,10 @@ void GCodeGenerator::process_layers( tbb::filter pipeline_to_string = cooling; if (m_find_replace) pipeline_to_string = pipeline_to_string & find_replace; + // It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline. // Handler is unregistered when the destructor is called. TBBLocalesSetter locales_setter; - // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); tbb::parallel_pipeline(12, pipeline_to_layerresult & pipeline_to_string & output); @@ -1668,10 +1683,10 @@ void GCodeGenerator::process_layers( tbb::filter pipeline_to_string = cooling; if (m_find_replace) pipeline_to_string = pipeline_to_string & find_replace; + // It registers a handler that sets locales to "C" before any TBB thread starts participating in tbb::parallel_pipeline. // Handler is unregistered when the destructor is called. TBBLocalesSetter locales_setter; - // The pipeline elements are joined using const references, thus no copying is performed. output_stream.find_replace_supress(); tbb::parallel_pipeline(12, pipeline_to_layerresult & pipeline_to_string & output); @@ -1709,6 +1724,7 @@ std::string GCodeGenerator::placeholder_parser_process( throw Slic3r::PlaceholderParserError(format("\"%s\" custom G-code needs to be added to s_CustomGcodeSpecificOptions", name.c_str())); } #endif + PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { ppi.update_from_gcodewriter(m_writer, m_print->wipe_tower_data()); @@ -1979,9 +1995,9 @@ namespace ProcessLayer { static std::string emit_custom_color_change_gcode_per_print_z( - GCodeGenerator &gcodegen, + GCodeGenerator &gcodegen, const CustomGCode::Item &custom_gcode, - unsigned int current_extruder_id, + unsigned int current_extruder_id, unsigned int first_extruder_id, // ID of the first extruder printing this layer. const PrintConfig &config ) { @@ -1996,28 +2012,30 @@ static std::string emit_custom_color_change_gcode_per_print_z( color_change_extruder = custom_gcode.extruder - 1; assert(color_change_extruder >= 0); - // Color Change or Tool Change as Color Change. - // add tag for processor + // Color Change or Tool Change as Color Change. + // add tag for processor gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Color_Change) + ",T" + std::to_string(color_change_extruder) + "," + custom_gcode.color + "\n"; DynamicConfig cfg; cfg.set_key_value("color_change_extruder", new ConfigOptionInt(color_change_extruder)); if (single_extruder_multi_material && !single_extruder_printer && color_change_extruder >= 0 && first_extruder_id != unsigned(color_change_extruder)) { - //! FIXME_in_fw show message during print pause + //! FIXME_in_fw show message during print pause // FIXME: Why is pause_print_gcode here? Why is it supplied "color_change_extruder"? - gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); - gcode += "\n"; + gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); + gcode += "\n"; gcode += "M117 Change filament for Extruder " + std::to_string(color_change_extruder) + "\n"; } else { gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id, &cfg); - gcode += "\n"; - //FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after - // return from M600. Thus the G-code generated by the following line is ignored. - // see GH issue #6362 - gcodegen.writer().unretract(); - } + gcode += "\n"; + //FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after + // return from M600. Thus the G-code generated by the following line is ignored. + // see GH issue #6362 + gcodegen.writer().unretract(); + } + return gcode; - } +} + static std::string emit_custom_gcode_per_print_z( GCodeGenerator &gcodegen, const CustomGCode::Item &custom_gcode, @@ -2025,7 +2043,7 @@ static std::string emit_custom_color_change_gcode_per_print_z( // ID of the first extruder printing this layer. unsigned int first_extruder_id, const PrintConfig &config) - { + { std::string gcode; // Extruder switches are processed by LayerTools, they should be filtered out. @@ -2043,25 +2061,26 @@ static std::string emit_custom_color_change_gcode_per_print_z( } else { if (gcode_type == CustomGCode::PausePrint) { // Pause print const std::string pause_print_msg = custom_gcode.extra; - // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n"; - //! FIXME_in_fw show message during print pause - if (!pause_print_msg.empty()) - gcode += "M117 " + pause_print_msg + "\n"; + + // add tag for processor + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Pause_Print) + "\n"; + //! FIXME_in_fw show message during print pause + if (!pause_print_msg.empty()) + gcode += "M117 " + pause_print_msg + "\n"; + DynamicConfig cfg; cfg.set_key_value("color_change_extruder", new ConfigOptionInt(int(current_extruder_id))); gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg); } else { - // add tag for processor - gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n"; - if (gcode_type == CustomGCode::Template) // Template Custom Gcode - gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id); - else // custom Gcode + // add tag for processor + gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Custom_Code) + "\n"; + if (gcode_type == CustomGCode::Template) // Template Custom Gcode + gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id); + else // custom Gcode gcode += custom_gcode.extra; - - } - gcode += "\n"; } + gcode += "\n"; + } return gcode; } @@ -2146,6 +2165,7 @@ bool GCodeGenerator::line_distancer_is_required(const std::vector& } return false; } + Polyline GCodeGenerator::get_layer_change_xy_path(const Vec3d &from, const Vec3d &to) { bool could_be_wipe_disabled{false}; @@ -2233,6 +2253,7 @@ std::string GCodeGenerator::generate_ramping_layer_change_gcode( const GCode::Impl::Travels::ElevatedTravelParams &elevation_params ) const { using namespace GCode::Impl::Travels; + const std::vector ensure_points_at_distances = linspace( elevation_params.slope_end - elevation_params.blend_width / 2.0, elevation_params.slope_end + elevation_params.blend_width / 2.0, @@ -2246,7 +2267,7 @@ std::string GCodeGenerator::generate_ramping_layer_change_gcode( std::string travel_gcode; Vec3d previous_point{this->point_to_gcode(travel.front())}; - for (const Vec3crd& point : travel) { + for (const Vec3crd &point : travel) { const Vec3d gcode_point{this->point_to_gcode(point)}; travel_gcode += this->m_writer .get_travel_to_xyz_gcode(previous_point, gcode_point, "layer change"); @@ -2254,6 +2275,7 @@ std::string GCodeGenerator::generate_ramping_layer_change_gcode( } return travel_gcode; } + // In sequential mode, process_layer is called once per each object and its copy, // therefore layers will contain a single entry and single_object_instance_idx will point to the copy of the object. // In non-sequential mode, process_layer is called per each print_z height with all object and support layers accumulated. @@ -2330,6 +2352,7 @@ LayerResult GCodeGenerator::process_layer( m_enable_loop_clipping = !enable; } + std::string gcode; assert(is_decimal_separator_point()); // for the sprintfs @@ -2361,10 +2384,11 @@ LayerResult GCodeGenerator::process_layer( print.config().before_layer_gcode.value, m_writer.extruder()->id(), &config) + "\n"; } - gcode += this->change_layer(previous_layer_z, print_z, result.spiral_vase_enable); // this will increase m_layer_index + gcode += this->change_layer(previous_layer_z, print_z, result.spiral_vase_enable); // this will increase m_layer_index m_layer = &layer; if (this->line_distancer_is_required(layer_tools.extruders) && this->m_layer != nullptr && this->m_layer->lower_layer != nullptr) m_travel_obstacle_tracker.init_layer(layer, layers); + m_object_layer_over_raft = false; if (!first_layer && ! print.config().layer_gcode.value.empty()) { DynamicConfig config; @@ -2452,11 +2476,13 @@ LayerResult GCodeGenerator::process_layer( // Now we have picked the right extruder, so we can emit the custom g-code. gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, *layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config()); } + if (auto loops_it = skirt_loops_per_extruder.find(extruder_id); loops_it != skirt_loops_per_extruder.end()) { if (!this->m_config.complete_objects.value) { gcode += this->m_label_objects.maybe_stop_instance(); } this->m_label_objects.update(nullptr); + const std::pair loops = loops_it->second; this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); @@ -2478,10 +2504,12 @@ LayerResult GCodeGenerator::process_layer( // Extrude brim with the extruder of the 1st region. if (! m_brim_done) { + if (!this->m_config.complete_objects.value) { gcode += this->m_label_objects.maybe_stop_instance(); } this->m_label_objects.update(nullptr); + this->set_origin(0., 0.); m_avoid_crossing_perimeters.use_external_mp(); for (const ExtrusionEntity *ee : print.brim().entities) @@ -2491,7 +2519,6 @@ LayerResult GCodeGenerator::process_layer( // Allow a straight travel move to the first object point. m_avoid_crossing_perimeters.disable_once(); } - this->m_label_objects.update(first_instance); // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature): @@ -2515,6 +2542,7 @@ LayerResult GCodeGenerator::process_layer( is_anything_overridden, false /* print_wipe_extrusions */); } + // During layer change the starting position of next layer is now known. // The solution is thus to emplace a temporary tag to the gcode, cache the postion and // replace the tag later. The tag is Layer_Change_Travel, the cached position is @@ -2582,6 +2610,7 @@ LayerResult GCodeGenerator::process_layer( } boost::algorithm::replace_first(gcode, tag, layer_change_gcode); + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << log_memory_info(); @@ -2640,7 +2669,6 @@ void GCodeGenerator::process_layer_single_object( const PrintObject &print_object = print_instance.print_object; const Print &print = *print_object.print(); - if (! print_wipe_extrusions && layer_to_print.support_layer != nullptr) if (const SupportLayer &support_layer = *layer_to_print.support_layer; ! support_layer.support_fills.entities.empty()) { ExtrusionRole role = support_layer.support_fills.role(); @@ -2775,7 +2803,7 @@ void GCodeGenerator::process_layer_single_object( } for (const ExtrusionEntity *ee : *eec) { // Don't reorder, don't flip. - gcode += this->extrude_entity({ *ee, false }, smooth_path_cache, comment_perimeter, -1.); + gcode += this->extrude_entity({*ee, false}, smooth_path_cache, comment_perimeter, -1.); m_travel_obstacle_tracker.mark_extruded(ee, print_instance.object_layer_to_print_id, print_instance.instance_id); } } @@ -2814,11 +2842,8 @@ void GCodeGenerator::process_layer_single_object( } } } - - } - void GCodeGenerator::apply_print_config(const PrintConfig &print_config) { m_writer.apply_print_config(print_config); @@ -2826,7 +2851,7 @@ void GCodeGenerator::apply_print_config(const PrintConfig &print_config) m_scaled_resolution = scaled(print_config.gcode_resolution.value); } -void GCodeGenerator::append_full_config(const Print &print, std::string &str) +void GCodeGenerator::append_full_config(const Print& print, std::string &str) { std::vector> config; encode_full_config(print, config); @@ -2837,7 +2862,7 @@ void GCodeGenerator::append_full_config(const Print &print, std::string &str) void GCodeGenerator::encode_full_config(const Print& print, std::vector>& config) { - const DynamicPrintConfig &cfg = print.full_print_config(); + const DynamicPrintConfig& cfg = print.full_print_config(); // Sorted list of config keys, which shall not be stored into the G-code. Initializer list. static constexpr auto banned_keys = { "compatible_printers"sv, @@ -2848,12 +2873,12 @@ void GCodeGenerator::encode_full_config(const Print& print, std::vectoris_nil()) + if (!is_banned(key) && !cfg.option(key)->is_nil()) config.emplace_back(key, cfg.opt_serialize(key)); } config.shrink_to_fit(); @@ -2862,7 +2887,6 @@ void GCodeGenerator::encode_full_config(const Print& print, std::vector &extruder_ids) { m_writer.set_extruders(extruder_ids); - m_wipe.init(this->config(), extruder_ids); } @@ -2872,6 +2896,7 @@ void GCodeGenerator::set_origin(const Vec2d &pointf) const auto offset = Point::new_scale(m_origin - pointf); if (last_position.has_value()) *(this->last_position) += offset; + m_wipe.offset_path(offset); m_origin = pointf; } @@ -2889,7 +2914,6 @@ std::string GCodeGenerator::preamble() return gcode; } - // called by GCodeGenerator::process_layer() std::string GCodeGenerator::change_layer( coordf_t previous_layer_z, @@ -2904,6 +2928,7 @@ std::string GCodeGenerator::change_layer( if (m_writer.multiple_extruders) { gcode += m_label_objects.maybe_change_instance(m_writer); } + if (!EXTRUDER_CONFIG(travel_ramping_lift) && EXTRUDER_CONFIG(retract_layer_change)) { gcode += this->retract_and_wipe(); } else if (EXTRUDER_CONFIG(travel_ramping_lift) && !vase_mode){ @@ -2931,6 +2956,7 @@ std::string GCodeGenerator::change_layer( // forget last wiping path as wiping after raising Z is pointless m_wipe.reset_path(); + return gcode; } @@ -2945,10 +2971,11 @@ static inline bool validate_smooth_path(const GCode::SmoothPath &smooth_path, bo return true; } #endif //NDEBUG + static constexpr const double min_gcode_segment_length = 0.002; + std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { - // Extrude all loops CCW. //w38 //bool is_hole = loop_src.is_clockwise(); @@ -2966,9 +2993,8 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC //w38 seam_point = m_seam_placer.place_seam(m_layer, new_loop_src, m_config.external_perimeters_first, seam_point); } - - // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, - // thus empty path segments will not be produced by G-code export. + // Because the G-code export has 1um resolution, don't generate segments shorter than 1.5 microns, + // thus empty path segments will not be produced by G-code export. //w38 GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( new_loop_src, is_hole, m_scaled_resolution, seam_point, scaled(0.0015)); @@ -2982,32 +3008,38 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC if (smooth_path.empty()) return {}; + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); // Apply the small perimeter speed. - //w38 - if (new_loop_src.paths.front().role().is_perimeter() && new_loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1) + //w38//Y27 + bool is_small_perimeter_length = false; + if (new_loop_src.paths.front().role().is_perimeter() && new_loop_src.length() <= SMALL_PERIMETER_LENGTH && speed == -1) { speed = m_config.small_perimeter_speed.get_abs_value(m_config.perimeter_speed); + is_small_perimeter_length = true; + } // Extrude along the smooth path. std::string gcode; - for (const GCode::SmoothPathElement &el : smooth_path) +//Y27 + for (const GCode::SmoothPathElement &el : smooth_path) { + m_resonance_avoidance = !is_small_perimeter_length; gcode += this->_extrude(el.path_attributes, el.path, description, speed); + } // reset acceleration gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); if (m_wipe.enabled()) { - // Wipe will hide the seam. m_wipe.set_path(std::move(smooth_path)); - }//w38 + } + //w38 else if (new_loop_src.paths.back().role().is_external_perimeter() && m_layer != nullptr && m_config.perimeters.value > 1) { // Only wipe inside if the wipe along the perimeter is disabled. // Make a little move inwards before leaving loop. if (std::optional pt = wipe_hide_seam(smooth_path, is_hole, scale_(EXTRUDER_CONFIG(nozzle_diameter))); pt) { - // Generate the seam hiding travel move. gcode += m_writer.travel_to_xy(this->point_to_gcode(*pt), "move inwards before travel"); this->last_position = *pt; @@ -3016,6 +3048,7 @@ std::string GCodeGenerator::extrude_loop(const ExtrusionLoop &loop_src, const GC return gcode; } + std::string GCodeGenerator::extrude_skirt( const ExtrusionLoop &loop_src, const ExtrusionFlow &extrusion_flow_override, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) @@ -3024,13 +3057,16 @@ std::string GCodeGenerator::extrude_skirt( Point seam_point = this->last_position.has_value() ? *this->last_position : Point::Zero(); GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit_split_with_seam( loop_src, false, m_scaled_resolution, seam_point, scaled(0.0015)); + // Clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case. if (m_enable_loop_clipping) clip_end(smooth_path, scale_(EXTRUDER_CONFIG(nozzle_diameter)) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER, scaled(min_gcode_segment_length)); + if (smooth_path.empty()) return {}; + assert(validate_smooth_path(smooth_path, ! m_enable_loop_clipping)); // Extrude along the smooth path. @@ -3041,8 +3077,10 @@ std::string GCodeGenerator::extrude_skirt( el.path_attributes.height = extrusion_flow_override.height; gcode += this->_extrude(el.path_attributes, el.path, description, speed); } + // reset acceleration gcode += m_writer.set_print_acceleration(fast_round_up(m_config.default_acceleration.value)); + if (m_wipe.enabled()) // Wipe will hide the seam. m_wipe.set_path(std::move(smooth_path)); @@ -3053,19 +3091,21 @@ std::string GCodeGenerator::extrude_skirt( std::string GCodeGenerator::extrude_multi_path(const ExtrusionMultiPath &multipath, bool reverse, const GCode::SmoothPathCache &smooth_path_cache, const std::string_view description, double speed) { #ifndef NDEBUG - for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++it) { + for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++ it) { assert(it->polyline.points.size() >= 2); assert(std::prev(it)->polyline.last_point() == it->polyline.first_point()); } #endif // NDEBUG GCode::SmoothPath smooth_path = smooth_path_cache.resolve_or_fit(multipath, reverse, m_scaled_resolution); - // extrude along the path + // extrude along the path std::string gcode; for (GCode::SmoothPathElement &el : smooth_path) gcode += this->_extrude(el.path_attributes, el.path, description, speed); + GCode::reverse(smooth_path); m_wipe.set_path(std::move(smooth_path)); + // reset acceleration gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.default_acceleration.value + 0.5)); return gcode; @@ -3114,9 +3154,9 @@ std::string GCodeGenerator::extrude_support(const ExtrusionEntityReferences &sup gcode += this->extrude_path(*path, eref.flipped(), smooth_path_cache, label, speed); else if (const ExtrusionMultiPath *multipath = dynamic_cast(&eref.extrusion_entity()); multipath) gcode += this->extrude_multi_path(*multipath, eref.flipped(), smooth_path_cache, label, speed); - else { + else { const ExtrusionEntityCollection *eec = dynamic_cast(&eref.extrusion_entity()); - assert(eec); + assert(eec); if (eec) { //FIXME maybe order the support here? ExtrusionEntityReferences refs; @@ -3214,33 +3254,37 @@ std::string GCodeGenerator::travel_to_first_position(const Vec3crd& point, const this->m_layer_change_used_external_mp = this->m_avoid_crossing_perimeters.use_external_mp_once; this->m_layer_change_layer = this->layer(); this->m_layer_change_origin = this->origin(); - double lift{ - EXTRUDER_CONFIG(travel_ramping_lift) ? EXTRUDER_CONFIG(travel_max_lift) : - EXTRUDER_CONFIG(retract_lift)}; - const double upper_limit = EXTRUDER_CONFIG(retract_lift_below); - const double lower_limit = EXTRUDER_CONFIG(retract_lift_above); - if ((lower_limit > 0 && gcode_point.z() < lower_limit) || - (upper_limit > 0 && gcode_point.z() > upper_limit)) { - lift = 0.0; - } + + double lift{ + EXTRUDER_CONFIG(travel_ramping_lift) ? EXTRUDER_CONFIG(travel_max_lift) : + EXTRUDER_CONFIG(retract_lift)}; + const double upper_limit = EXTRUDER_CONFIG(retract_lift_below); + const double lower_limit = EXTRUDER_CONFIG(retract_lift_above); + if ((lower_limit > 0 && gcode_point.z() < lower_limit) || + (upper_limit > 0 && gcode_point.z() > upper_limit)) { + lift = 0.0; + } if (EXTRUDER_CONFIG(retract_length) > 0 && !this->last_position) { - if (!this->last_position || EXTRUDER_CONFIG(retract_before_travel) < (this->point_to_gcode(*this->last_position) - gcode_point.head<2>()).norm()) { - gcode += this->writer().retract(); - gcode += this->writer().get_travel_to_z_gcode(from_z + lift, "lift"); + if (!this->last_position || EXTRUDER_CONFIG(retract_before_travel) < (this->point_to_gcode(*this->last_position) - gcode_point.head<2>()).norm()) { + gcode += this->writer().retract(); + gcode += this->writer().get_travel_to_z_gcode(from_z + lift, "lift"); + } } - } + const std::string comment{"move to first layer point"}; gcode += insert_gcode(); - gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment); - gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment); + gcode += this->writer().get_travel_to_xy_gcode(gcode_point.head<2>(), comment); + gcode += this->writer().get_travel_to_z_gcode(gcode_point.z(), comment); this->m_avoid_crossing_perimeters.reset_once_modifiers(); this->last_position = point.head<2>(); this->writer().update_position(gcode_point); } + m_current_layer_first_position = gcode_point; + return gcode; } @@ -3257,40 +3301,42 @@ double cap_speed( } return speed; } + std::string GCodeGenerator::_extrude( - const ExtrusionAttributes &path_attr, - const Geometry::ArcWelder::Path &path, + const ExtrusionAttributes &path_attr, + const Geometry::ArcWelder::Path &path, const std::string_view description, double speed) { std::string gcode; const std::string_view description_bridge = path_attr.role.is_bridge() ? " (bridge)"sv : ""sv; //w30 - bool is_first_or_bottom_layer = (path_attr.role == ExtrusionRole::TopSolidInfill) || (this->on_first_layer()); - bool is_first = this->on_first_layer(); + bool is_first_or_bottom_layer = (path_attr.role == ExtrusionRole::TopSolidInfill) || (this->on_first_layer()); + bool is_first = this->on_first_layer(); const bool has_active_instance{m_label_objects.has_active_instance()}; if (m_writer.multiple_extruders && has_active_instance) { gcode += m_label_objects.maybe_change_instance(m_writer); } + if (!m_current_layer_first_position) { const Vec3crd point = to_3d(path.front().point, scaled(this->m_last_layer_z)); gcode += this->travel_to_first_position(point, unscaled(point.z()), path_attr.role, [&](){ return m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer); }); } else { - // go to first point of extrusion path + // go to first point of extrusion path if (!this->last_position) { const double z = this->m_last_layer_z; - const std::string comment{"move to print after unknown position"}; - gcode += this->retract_and_wipe(); + const std::string comment{"move to print after unknown position"}; + gcode += this->retract_and_wipe(); gcode += m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer); - gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment); - gcode += this->m_writer.get_travel_to_z_gcode(z, comment); + gcode += this->m_writer.travel_to_xy(this->point_to_gcode(path.front().point), comment); + gcode += this->m_writer.get_travel_to_z_gcode(z, comment); } else if ( this->last_position != path.front().point) { - std::string comment = "move to first "; - comment += description; - comment += description_bridge; - comment += " point"; + std::string comment = "move to first "; + comment += description; + comment += description_bridge; + comment += " point"; const std::string travel_gcode{this->travel_to(*this->last_position, path.front().point, path_attr.role, comment, [&](){ return m_writer.multiple_extruders ? "" : m_label_objects.maybe_change_instance(m_writer); })}; @@ -3303,10 +3349,11 @@ std::string GCodeGenerator::_extrude( // compensate retraction if (this->m_already_unretracted) { - gcode += this->unretract(); + gcode += this->unretract(); } else { this->m_already_unretracted = true; gcode += "FIRST_UNRETRACT" + this->unretract(); + //First unretract may or may not be removed thus we must start from E0. gcode += this->writer().reset_e(); } @@ -3409,7 +3456,6 @@ std::string GCodeGenerator::_extrude( std::pair dynamic_speed_and_fan_speed{-1, -1}; if (path_attr.overhang_attributes.has_value()) { - double external_perim_reference_speed = m_config.get_abs_value("external_perimeter_speed"); if (external_perim_reference_speed == 0) external_perim_reference_speed = m_volumetric_speed / path_attr.mm3_per_mm; @@ -3421,22 +3467,33 @@ std::string GCodeGenerator::_extrude( external_perim_reference_speed, speed); } //Y27 - bool dont_speed_down_by_overhang = true; - if (dynamic_speed_and_fan_speed.first > -1) { - if (speed != dynamic_speed_and_fan_speed.first) { - dont_speed_down_by_overhang = false; + if (path_attr.role == ExtrusionRole::ExternalPerimeter && m_config.opt_bool("resonance_avoidance")) { + if (dynamic_speed_and_fan_speed.first > -1) { + if (speed != dynamic_speed_and_fan_speed.first) { + double min_speed = cap_speed(speed, path_attr.mm3_per_mm, m_config, m_writer.extruder()->id()); + if (min_speed > m_config.opt_float("max_resonance_avoidance_speed")) { + m_resonance_avoidance = false; + } + } speed = dynamic_speed_and_fan_speed.first; } + + // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) + speed = cap_speed(speed, path_attr.mm3_per_mm, m_config, m_writer.extruder()->id()); + + if (m_resonance_avoidance) { + if (speed <= m_config.opt_float("max_resonance_avoidance_speed")) { + speed = std::min(speed, m_config.opt_float("min_resonance_avoidance_speed")); + } + m_resonance_avoidance = true; + } + } else { + if (dynamic_speed_and_fan_speed.first > -1) { + speed = dynamic_speed_and_fan_speed.first; + } + speed = cap_speed(speed, path_attr.mm3_per_mm, m_config, m_writer.extruder()->id()); } - // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) - speed = cap_speed(speed, path_attr.mm3_per_mm, m_config, m_writer.extruder()->id()); -//Y27 - if (path_attr.role == ExtrusionRole::ExternalPerimeter && m_config.opt_bool("resonance_avoidance" ) && dont_speed_down_by_overhang) { - if (speed <= m_config.opt_float("max_resonance_avoidance_speed")) { - speed = std::min(speed, m_config.opt_float("min_resonance_avoidance_speed")); - } - } double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line @@ -3498,15 +3555,16 @@ std::string GCodeGenerator::_extrude( cooling_marker_setspeed_comments += ";_EXTERNAL_PERIMETER"; } - // F is mm per minute. - gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); + // F is mm per minute. + gcode += m_writer.set_speed(F, "", cooling_marker_setspeed_comments); if (dynamic_speed_and_fan_speed.second >= 0) gcode += ";_SET_FAN_SPEED" + std::to_string(int(dynamic_speed_and_fan_speed.second)) + "\n"; - std::string comment; - if (m_config.gcode_comments) { - comment = description; - comment += description_bridge; - } + + std::string comment; + if (m_config.gcode_comments) { + comment = description; + comment += description_bridge; + } Vec2d prev_exact = this->point_to_gcode(path.front().point); Vec2d prev = GCodeFormatter::quantize(prev_exact); auto it = path.begin(); @@ -3541,15 +3599,14 @@ std::string GCodeGenerator::_extrude( double angle = Geometry::ArcWelder::arc_angle(prev.cast(), p.cast(), double(radius)); assert(angle > 0); const double line_length = angle * std::abs(radius); - const double dE = e_per_mm * line_length; + const double dE = e_per_mm * line_length; assert(dE > 0); gcode += m_writer.extrude_to_xy_G2G3IJ(p, ij, it->ccw(), dE, comment); } - prev = p; + prev = p; prev_exact = p_exact; } } - if (m_enable_cooling_markers) gcode += path_attr.role.is_bridge() ? ";_BRIDGE_FAN_END\n" : ";_EXTRUDE_END\n"; @@ -3561,13 +3618,13 @@ std::string GCodeGenerator::_extrude( return gcode; } - std::string GCodeGenerator::generate_travel_gcode( const Points3& travel, const std::string& comment, const std::function& insert_gcode ) { std::string gcode; + const unsigned acceleration =(unsigned)(m_config.travel_acceleration.value + 0.5); if (travel.empty()) { @@ -3576,7 +3633,6 @@ std::string GCodeGenerator::generate_travel_gcode( // generate G-code for the travel move // use G1 because we rely on paths being straight (G0 may make round paths) - gcode += this->m_writer.set_travel_acceleration(acceleration); Vec3d previous_point{this->point_to_gcode(travel.front())}; @@ -3589,16 +3645,17 @@ std::string GCodeGenerator::generate_travel_gcode( gcode += insert_gcode(); already_inserted = true; } + gcode += this->m_writer.travel_to_xyz(previous_point, gcode_point, comment); this->last_position = point.head<2>(); previous_point = gcode_point; } - if (! GCodeWriter::supports_separate_travel_acceleration(config().gcode_flavor)) { - // In case that this flavor does not support separate print and travel acceleration, - // reset acceleration to default. + if (! GCodeWriter::supports_separate_travel_acceleration(config().gcode_flavor)) { + // In case that this flavor does not support separate print and travel acceleration, + // reset acceleration to default. gcode += this->m_writer.set_travel_acceleration(acceleration); - } + } return gcode; } @@ -3690,7 +3747,6 @@ std::string GCodeGenerator::travel_to( const std::string &comment, const std::function& insert_gcode ) { - // check whether a straight travel move would need retraction bool could_be_wipe_disabled {false}; @@ -3735,6 +3791,7 @@ std::string GCodeGenerator::travel_to( (upper_limit > 0 && initial_elevation > upper_limit)) { can_be_flat = true; } + const Points3 travel = ( can_be_flat ? GCode::Impl::Travels::generate_flat_travel(xy_path.points, initial_elevation) : @@ -3771,7 +3828,7 @@ std::string GCodeGenerator::retract_and_wipe(bool toolchange, bool reset_e) gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); if (reset_e) { - gcode += m_writer.reset_e(); + gcode += m_writer.reset_e(); } return gcode; @@ -3807,6 +3864,7 @@ std::string GCodeGenerator::set_extruder(unsigned int extruder_id, double print_ if (!this->m_config.complete_objects.value) { gcode += this->m_label_objects.maybe_stop_instance(); } + // prepend retraction on the current extruder gcode += this->retract_and_wipe(true); @@ -3976,7 +4034,6 @@ Point GCodeGenerator::gcode_to_point(const Vec2d &point) const // This function may be called at the very start from toolchange G-code when the extruder is not assigned yet. pt += m_config.extruder_offset.get_at(extruder->id()); return scaled(pt); - } } // namespace Slic3r diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 1b3f09d..9e852ea 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -456,6 +456,9 @@ private: // Processor GCodeProcessor m_processor; +//Y27 + bool m_resonance_avoidance; + // Back-pointer to Print (const). const Print* m_print; std::string _extrude( diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index b679e75..0839ce9 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -466,39 +466,42 @@ static std::vector s_Preset_print_options { "wipe_tower_width", "wipe_tower_cone_angle", "wipe_tower_rotation_angle", "wipe_tower_brim_width", "wipe_tower_bridging", "single_extruder_multi_material_priming", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth", "wipe_tower_extruder", "wipe_tower_no_sparse_layers", "wipe_tower_extra_flow", "wipe_tower_extra_spacing", "compatible_printers", "compatible_printers_condition", "inherits", "perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", - "wall_distribution_count", "min_feature_size", "min_bead_width" + "wall_distribution_count", "min_feature_size", "min_bead_width", //B36 - ,"first_layer_travel_speed" + "first_layer_travel_speed", //B37 - ,"first_layer_infill_speed" + "first_layer_infill_speed", //w11 - ,"detect_narrow_internal_solid_infill" + "detect_narrow_internal_solid_infill", //Y21 - ,"seam_gap" + "seam_gap", //w16 - , "top_one_wall_type" + "top_one_wall_type", //w17 - ,"top_area_threshold" + "top_area_threshold", //w21 - ,"filter_top_gap_infill" + "filter_top_gap_infill", //w23 - ,"only_one_wall_first_layer" + "only_one_wall_first_layer", //w25 - ,"slow_down_layers" + "slow_down_layers", //w26 - ,"elefant_foot_compensation_layers" + "elefant_foot_compensation_layers", //w27 - ,"precise_z_height" + "precise_z_height", //w28 - ,"max_bridge_length" + "max_bridge_length", //w30 - ,"top_solid_infill_flow_ratio", "bottom_solid_infill_flow_ratio" + "top_solid_infill_flow_ratio", "bottom_solid_infill_flow_ratio", //w33 - ,"ironing_pattern" + "ironing_pattern", //w38 - ,"overhang_reverse","overhang_reverse_internal_only","overhang_reverse_threshold" + "overhang_reverse", "overhang_reverse_internal_only", "overhang_reverse_threshold", //w39 - ,"precise_outer_wall"}; + "precise_outer_wall", + //Y27 + "resonance_avoidance", "min_resonance_avoidance_speed", "max_resonance_avoidance_speed" +}; static std::vector s_Preset_filament_options { "filament_colour", "filament_diameter", "filament_type", "filament_soluble", "filament_notes", "filament_max_volumetric_speed", @@ -567,8 +570,6 @@ static std::vector s_Preset_printer_options { "bed_exclude_area", //Y25 "wipe_device", -//Y27 - "resonance_avoidance", "min_resonance_avoidance_speed", "max_resonance_avoidance_speed", //Y16 "chamber_temperature", "auxiliary_fan", "chamber_fan" }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 81b42ed..819c2d6 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -228,10 +228,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n || opt_key == "single_extruder_multi_material" //Y25 || opt_key == "wipe_device" - //Y27 - || opt_key == "resonance_avoidance" - || opt_key == "min_resonance_avoidance_speed" - || opt_key == "max_resonance_avoidance_speed" || opt_key == "temperature" || opt_key == "idle_temperature" || opt_key == "wipe_tower" diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 53e4894..d9ae368 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -702,6 +702,30 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBools{false}); + //Y27 + def = this->add("resonance_avoidance", coBool); + def->label = L("Resonance avoidance"); + def->tooltip = L("By reducing the speed of the outer wall to avoid the resonance zone of the printer, ringing on the surface of the model are avoided.\n" + "Please turn this option off when testing ringing."); + def->mode = comExpert; + def->set_default_value(new ConfigOptionBool(true)); + + def = this->add("min_resonance_avoidance_speed", coFloat); + def->label = L("Min"); + def->tooltip = L("Minimum speed of resonance avoidance."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(70)); + + def = this->add("max_resonance_avoidance_speed", coFloat); + def->label = L("Max"); + def->tooltip = L("Maximum speed of resonance avoidance."); + def->sidetext = L("mm/s"); + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(120)); + // TRN FilamentSettings : "Dynamic fan speeds" auto fan_speed_setting_description = L("Overhang size is expressed as a percentage of overlap of the extrusion with the previous layer: " "100% would be full overlap (no overhang), while 0% represents full overhang (floating extrusion, bridge). " @@ -3007,30 +3031,6 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); -//Y27 - def = this->add("resonance_avoidance", coBool); - def->label = L("Resonance avoidance"); - def->tooltip = L("By reducing the speed of the outer wall to avoid the resonance zone of the printer, ringing on the surface of the model are avoided. " - "Turn this option off when testing ringing."); - def->mode = comExpert; - def->set_default_value(new ConfigOptionBool(true)); - - def = this->add("min_resonance_avoidance_speed", coFloat); - def->label = L("Min"); - def->tooltip = L("Minimum speed of resonance avoidance."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(70)); - - def = this->add("max_resonance_avoidance_speed", coFloat); - def->label = L("Max"); - def->tooltip = L("Maximum speed of resonance avoidance."); - def->sidetext = L("mm/s"); - def->min = 0; - def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(115)); - def = this->add("single_extruder_multi_material_priming", coBool); def->label = L("Prime all printing extruders"); def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print."); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index df256f4..01c1c91 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -626,6 +626,10 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloatOrPercent, overhang_speed_1)) ((ConfigOptionFloatOrPercent, overhang_speed_2)) ((ConfigOptionFloatOrPercent, overhang_speed_3)) + //Y27 + ((ConfigOptionBool, resonance_avoidance)) + ((ConfigOptionFloat, min_resonance_avoidance_speed)) + ((ConfigOptionFloat, max_resonance_avoidance_speed)) ((ConfigOptionBool, external_perimeters_first)) ((ConfigOptionBool, extra_perimeters)) ((ConfigOptionBool, extra_perimeters_on_overhangs)) @@ -785,10 +789,6 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, single_extruder_multi_material)) //Y25 ((ConfigOptionBool, wipe_device)) - //Y27 - ((ConfigOptionBool, resonance_avoidance)) - ((ConfigOptionFloat, min_resonance_avoidance_speed)) - ((ConfigOptionFloat, max_resonance_avoidance_speed)) ((ConfigOptionBool, single_extruder_multi_material_priming)) ((ConfigOptionBool, wipe_tower_no_sparse_layers)) ((ConfigOptionString, toolchange_gcode)) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 90a672e..735e23e 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -253,6 +253,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("overhang_speed_" + std::to_string(i), config->opt_bool("enable_dynamic_overhang_speeds")); } +//Y27 + bool resonance_avoidance = config->opt_bool("resonance_avoidance"); + toggle_field("min_resonance_avoidance_speed", resonance_avoidance); + toggle_field("max_resonance_avoidance_speed", resonance_avoidance); + bool have_infill = config->option("fill_density")->value > 0; // infill_extruder uses the same logic as in Print::extruders() for (auto el : { "fill_pattern", "infill_every_layers", "infill_only_where_needed", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6fa3f83..3ca3d59 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1625,6 +1625,14 @@ void TabPrint::build() optgroup->append_single_option_line("overhang_speed_2"); optgroup->append_single_option_line("overhang_speed_3"); + //Y27 + optgroup = page->new_optgroup(L("Resonance Avoidance")); + optgroup->append_single_option_line("resonance_avoidance"); + line = { L("Resonance Avoidance Speed"), "" }; + line.append_option(optgroup->get_option("min_resonance_avoidance_speed")); + line.append_option(optgroup->get_option("max_resonance_avoidance_speed")); + optgroup->append_line(line); + optgroup = page->new_optgroup(L("Speed for non-print moves")); optgroup->append_single_option_line("travel_speed"); optgroup->append_single_option_line("travel_speed_z"); @@ -2738,12 +2746,6 @@ void TabPrinter::build_fff() Option option(def, "extruders_count"); optgroup->append_single_option_line(option); optgroup->append_single_option_line("single_extruder_multi_material"); -//Y27 - optgroup->append_single_option_line("resonance_avoidance"); - Line line = { L("Resonance Avoidance Speed"), "" }; - line.append_option(optgroup->get_option("min_resonance_avoidance_speed")); - line.append_option(optgroup->get_option("max_resonance_avoidance_speed")); - optgroup->append_line(line); optgroup->m_on_change = [this, optgroup_wk = ConfigOptionsGroupWkp(optgroup)](t_config_option_key opt_key, boost::any value) { auto optgroup_sh = optgroup_wk.lock(); @@ -3615,10 +3617,6 @@ void TabPrinter::toggle_options() toggle_option("toolchange_gcode", have_multiple_extruders); if (m_active_page->title() == "General") { toggle_option("single_extruder_multi_material", have_multiple_extruders); -//Y27 - bool resonance_avoidance = m_config->opt_bool("resonance_avoidance"); - toggle_option("min_resonance_avoidance_speed", resonance_avoidance); - toggle_option("max_resonance_avoidance_speed", resonance_avoidance); bool is_marlin_flavor = flavor == gcfMarlinLegacy || flavor == gcfMarlinFirmware; // Disable silent mode for non-marlin firmwares.