diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index c371d98b45..fc903d13bf 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -289,6 +289,7 @@ static constexpr const char* TIMELAPSE_TYPE_ATTR = "timelapse_type"; static constexpr const char* OUTSIDE_ATTR = "outside"; static constexpr const char* SUPPORT_USED_ATTR = "support_used"; static constexpr const char* LABEL_OBJECT_ENABLED_ATTR = "label_object_enabled"; +static constexpr const char* TIMELAPSE_TYPE_ATTR = "timelapse_type"; static constexpr const char* SKIPPED_ATTR = "skipped"; static constexpr const char* OBJECT_TYPE = "object"; @@ -5255,7 +5256,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _add_project_embedded_presets_to_archive(mz_zip_archive& archive, Model& model, std::vector project_presets); bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, const ObjectToObjectDataMap &objects_data, int export_plate_idx = -1, bool save_gcode = true, bool use_loaded_id = false); bool _add_cut_information_file_to_archive(mz_zip_archive &archive, Model &model); - bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config); + bool _add_slice_info_config_file_to_archive(mz_zip_archive &archive, const Model &model, PlateDataPtrs &plate_data_list, const ObjectToObjectDataMap &objects_data, const DynamicPrintConfig& config); bool _add_gcode_file_to_archive(mz_zip_archive& archive, const Model& model, PlateDataPtrs& plate_data_list, Export3mfProgressFn proFn = nullptr); bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config); bool _add_auxiliary_dir_to_archive(mz_zip_archive &archive, const std::string &aux_dir, PackingTemporaryData &data); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a3c7667e31..625ec0c55b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -736,6 +736,33 @@ static std::vector get_path_of_change_filament(const Print& print) return gcode; } + bool WipeTowerIntegration::is_empty_wipe_tower_gcode(GCode &gcodegen, int extruder_id, bool finish_layer) + { + assert(m_layer_idx >= 0); + if (m_layer_idx >= (int) m_tool_changes.size()) + return true; + + bool ignore_sparse = false; + if (gcodegen.config().wipe_tower_no_sparse_layers.value) { + ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool); + } + + if (m_enable_timelapse_print && m_is_first_print) { + return false; + } + + if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { + if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size())) + throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer."); + + if (!ignore_sparse) { + return false; + } + } + + return true; + } + // Print is finished. Now it remains to unload the filament safely with ramming over the wipe tower. std::string WipeTowerIntegration::finalize(GCode& gcodegen) { @@ -1011,6 +1038,7 @@ namespace DoExport { if (ret.size() < MAX_TAGS_COUNT) check(_(L("Machine end G-code")), config.machine_end_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Before layer change G-code")), config.before_layer_change_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Layer change G-code")), config.layer_change_gcode.value); + if (ret.size() < MAX_TAGS_COUNT) check(_(L("Time lapse G-code")), config.time_lapse_gcode.value); if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change filament G-code")), config.change_filament_gcode.value); //BBS //if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value); @@ -1132,6 +1160,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu BOOST_LOG_TRIVIAL(debug) << "Start processing gcode, " << log_memory_info(); // Post-process the G-code to update time stamps. + + m_processor.result().timelapse_warning_code = m_timelapse_warning_code; m_processor.finalize(true); // DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics); @@ -2827,10 +2857,35 @@ GCode::LayerResult GCode::process_layer( + "\n"; } + PrinterStructure printer_structure = m_config.printer_structure.value; + bool need_insert_timelapse_gcode_for_traditional = false; + if (printer_structure == PrinterStructure::psI3 && (!m_wipe_tower || !m_wipe_tower->enable_timelapse_print())) { + need_insert_timelapse_gcode_for_traditional = true; + } + bool has_insert_timelapse_gcode = false; + bool has_wipe_tower = (layer_tools.has_wipe_tower && m_wipe_tower); + + auto insert_timelapse_gcode = [this, print_z, &print]() -> std::string { + std::string gcode_res; + if (!print.config().time_lapse_gcode.value.empty()) { + DynamicConfig config; + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + gcode_res = this->placeholder_parser_process("timelapse_gcode", print.config().time_lapse_gcode.value, m_writer.extruder()->id(), &config) + "\n"; + config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); + } + return gcode_res; + }; + // BBS: don't use lazy_raise when enable spiral vase gcode += this->change_layer(print_z); // this will increase m_layer_index m_layer = &layer; m_object_layer_over_raft = false; + if (printer_structure == PrinterStructure::psI3 && !need_insert_timelapse_gcode_for_traditional) { + gcode += insert_timelapse_gcode(); + //todo: get the last position of timelapse_gcode, and set into m_writer. Then delete the m_writer.set_current_position_clear(false) + m_writer.set_current_position_clear(false); + } if (! print.config().layer_change_gcode.value.empty()) { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); @@ -3137,9 +3192,21 @@ GCode::LayerResult GCode::process_layer( // Extrude the skirt, brim, support, perimeters, infill ordered by the extruders. for (unsigned int extruder_id : layer_tools.extruders) { - gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ? - m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : - this->set_extruder(extruder_id, print_z); + if (has_wipe_tower) { + if (!m_wipe_tower->is_empty_wipe_tower_gcode(*this, extruder_id, extruder_id == layer_tools.extruders.back())) { + if (need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode) { + gcode += this->retract(false, false, LiftType::NormalLift); + m_writer.add_object_change_labels(gcode); + gcode += insert_timelapse_gcode(); + //todo: get the last position of timelapse_gcode, and set into m_writer. Then delete the m_writer.set_current_position_clear(false) + m_writer.set_current_position_clear(false); + has_insert_timelapse_gcode = true; + } + gcode += m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()); + } + } else { + gcode += this->set_extruder(extruder_id, print_z); + } // let analyzer tag generator aware of a role type change if (layer_tools.has_wipe_tower && m_wipe_tower) @@ -3234,12 +3301,15 @@ GCode::LayerResult GCode::process_layer( m_object_layer_over_raft = object_layer_over_raft; if (m_config.reduce_crossing_wall) m_avoid_crossing_perimeters.init_layer(*m_layer); + + 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"; if (print.is_BBL_Printer()) { start_str += ("M624 " + _encode_label_ids_to_base64({ instance_to_print.label_object_id })); start_str += "\n"; } + temp_start_str = start_str; m_writer.set_object_start_str(start_str); } //Orca's implementation for skipping object, for klipper firmware printer only @@ -3325,13 +3395,58 @@ GCode::LayerResult GCode::process_layer( //FIXME the following code prints regions in the order they are defined, the path is not optimized in any way. bool is_infill_first = print.config().wall_infill_order == WallInfillOrder::InfillInnerOuter || print.config().wall_infill_order == WallInfillOrder::InfillOuterInner; + + auto has_infill = [](const std::vector &by_region) { + for (auto region : by_region) { + if (!region.infills.empty()) + return true; + } + return false; + }; + //BBS: for first layer, we always print wall firstly to get better bed adhesive force //This behaviour is same with cura if (is_infill_first && !first_layer) { + if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode && has_infill(by_region_specific)) { + gcode += this->retract(false, false, LiftType::NormalLift); + if (!temp_start_str.empty() && m_writer.empty_object_start_str()) { + std::string end_str = std::string("; stop printing object, unique label id: ") + std::to_string(instance_to_print.label_object_id) + "\n"; + if (print.is_BBL_Printer()) + end_str += "M625\n"; + gcode += end_str; + } + + gcode += insert_timelapse_gcode(); + //todo: get the last position of timelapse_gcode, and set into m_writer. Then delete the m_writer.set_current_position_clear(false) + m_writer.set_current_position_clear(false); + + if (!temp_start_str.empty() && m_writer.empty_object_start_str()) + gcode += temp_start_str; + temp_start_str.clear(); + has_insert_timelapse_gcode = true; + } gcode += this->extrude_infill(print, by_region_specific, false); gcode += this->extrude_perimeters(print, by_region_specific); } else { gcode += this->extrude_perimeters(print, by_region_specific); + if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode && has_infill(by_region_specific)) { + gcode += this->retract(false, false, LiftType::NormalLift); + if (!temp_start_str.empty() && m_writer.empty_object_start_str()) { + std::string end_str = std::string("; stop printing object, unique label id: ") + std::to_string(instance_to_print.label_object_id) + "\n"; + if (print.is_BBL_Printer()) + end_str += "M625\n"; + gcode += end_str; + } + + gcode += insert_timelapse_gcode(); + //todo: get the last position of timelapse_gcode, and set into m_writer. Then delete the m_writer.set_current_position_clear(false) + m_writer.set_current_position_clear(false); + + if (!temp_start_str.empty() && m_writer.empty_object_start_str()) + gcode += temp_start_str; + temp_start_str.clear(); + has_insert_timelapse_gcode = true; + } gcode += this->extrude_infill(print,by_region_specific, false); } // ironing @@ -3387,6 +3502,16 @@ GCode::LayerResult GCode::process_layer( BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << log_memory_info(); + if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode) { + if (m_timelapse_warning_code == 0) + m_timelapse_warning_code = 1; + gcode += this->retract(false, false, LiftType::NormalLift); + m_writer.add_object_change_labels(gcode); + gcode += insert_timelapse_gcode(); + //todo: get the last position of timelapse_gcode, and set into m_writer. Then delete the m_writer.set_current_position_clear(false) + m_writer.set_current_position_clear(false); + } + result.gcode = std::move(gcode); result.cooling_buffer_flush = object_layer || raft_layer || last_layer; return result; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 5f7b00154f..3c8fefc73a 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -91,6 +91,7 @@ public: std::string prime(GCode &gcodegen); void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); + bool is_empty_wipe_tower_gcode(GCode &gcodegen, int extruder_id, bool finish_layer); std::string finalize(GCode &gcodegen); std::vector used_filament_length() const; @@ -490,6 +491,8 @@ private: std::vector m_label_objects_ids; std::string _encode_label_ids_to_base64(std::vector ids); + int m_timelapse_warning_code = 0; + bool m_silent_time_estimator_enabled; // Processor diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 2ff3dc4cec..a67c489a2e 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -788,6 +788,7 @@ void GCodeProcessorResult::reset() { toolpath_outside = false; //BBS: add label_object_enabled label_object_enabled = false; + timelapse_warning_code = 0; printable_height = 0.0f; settings_ids.reset(); extruders_count = 0; @@ -815,6 +816,7 @@ void GCodeProcessorResult::reset() { toolpath_outside = false; //BBS: add label_object_enabled label_object_enabled = false; + timelapse_warning_code = 0; printable_height = 0.0f; settings_ids.reset(); extruders_count = 0; @@ -4320,6 +4322,15 @@ void GCodeProcessor::update_slice_warnings() m_result.warnings.push_back(warning); } + // bbs:HRC checker + warning.params.clear(); + warning.level = 1; + if (m_result.timelapse_warning_code != 0) { + warning.msg = NOT_SUPPORT_TRADITIONAL_TIMELAPSE; + warning.error_code = "1000C003"; + m_result.warnings.push_back(warning); + } + m_result.warnings.shrink_to_fit(); } diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 4ee1ce700b..2ee9cc2d08 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -20,6 +20,7 @@ namespace Slic3r { // slice warnings enum strings #define NOZZLE_HRC_CHECKER "the_actual_nozzle_hrc_smaller_than_the_required_nozzle_hrc" #define BED_TEMP_TOO_HIGH_THAN_FILAMENT "bed_temperature_too_high_than_filament" +#define NOT_SUPPORT_TRADITIONAL_TIMELAPSE "not_support_traditional_timelapse" enum class EMoveType : unsigned char { @@ -179,6 +180,7 @@ namespace Slic3r { bool toolpath_outside; //BBS: add object_label_enabled bool label_object_enabled; + int timelapse_warning_code {0}; float printable_height; SettingsIds settings_ids; size_t extruders_count; @@ -211,6 +213,7 @@ namespace Slic3r { bed_exclude_area = other.bed_exclude_area; toolpath_outside = other.toolpath_outside; label_object_enabled = other.label_object_enabled; + timelapse_warning_code = other.timelapse_warning_code; printable_height = other.printable_height; settings_ids = other.settings_ids; extruders_count = other.extruders_count; @@ -709,6 +712,7 @@ namespace Slic3r { void reset(); const GCodeProcessorResult& get_result() const { return m_result; } + GCodeProcessorResult& result() { return m_result; } GCodeProcessorResult&& extract_result() { return std::move(m_result); } // Load a G-code into a stand-alone G-code viewer. diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 0e35accc5b..7b178fc389 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -846,7 +846,7 @@ static std::vector s_Preset_machine_limits_options { static std::vector s_Preset_printer_options { "printer_technology", "printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", - "single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "change_filament_gcode", + "single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_max_radius","extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", "default_print_profile", "inherits", "silent_mode", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 7b89cf2022..78624782b2 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -117,6 +117,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "textured_plate_temp_initial_layer", "gcode_add_line_number", "layer_change_gcode", + "time_lapse_gcode", "fan_min_speed", "fan_max_speed", "printable_height", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 2579babf62..e92254cc12 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2011,6 +2011,14 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionString("")); + def = this->add("time_lapse_gcode",coString); + def->label = L("Time lapse G-code"); + def->multiline = true; + def->full_width = true; + def->height =5; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionString("")); + def = this->add("silent_mode", coBool); def->label = L("Supports silent mode"); def->tooltip = L("Whether the machine supports silent mode in which machine use lower acceleration to print"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index e04a783fd9..95f8fa4e12 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -857,6 +857,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, bbl_bed_temperature_gcode)) ((ConfigOptionEnum, gcode_flavor)) ((ConfigOptionString, layer_change_gcode)) + ((ConfigOptionString, time_lapse_gcode)) //#ifdef HAS_PRESSURE_EQUALIZER // ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_positive)) // ((ConfigOptionFloat, max_volumetric_extrusion_rate_slope_negative)) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index eabc620adb..193802840b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2839,6 +2839,8 @@ wxString Plater::get_slice_warning_string(GCodeProcessorResult::SliceWarning& wa return _L("The bed temperature exceeds filament's vitrification temperature. Please open the front door of printer before printing to avoid nozzle clog."); } else if (warning.msg == NOZZLE_HRC_CHECKER) { return _L("The nozzle hardness required by the filament is higher than the default nozzle hardness of the printer. Please replace the hardened nozzle or filament, otherwise, the nozzle will be attrited or damaged."); + } else if (warning.msg == NOT_SUPPORT_TRADITIONAL_TIMELAPSE) { + return _L("Enable traditional timelapse will cause artifacts on this model."); } else { return wxString(warning.msg); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 827f8c119f..87b7506f34 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3154,6 +3154,16 @@ void TabPrinter::build_fff() option.opt.is_code = true; option.opt.height = gcode_field_height;//150; optgroup->append_single_option_line(option); + + optgroup = page->new_optgroup(L("Time lapse G-code"), L"param_gcode", 0); + optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { + validate_custom_gcode_cb(this, optgroup, opt_key, value); + }; + option = optgroup->get_option("time_lapse_gcode"); + option.opt.full_width = true; + option.opt.is_code = true; + option.opt.height = gcode_field_height;//150; + optgroup->append_single_option_line(option); optgroup = page->new_optgroup(L("Change filament G-code"), L"param_gcode", 0); optgroup->m_on_change = [this, optgroup](const t_config_option_key& opt_key, const boost::any& value) { @@ -3680,6 +3690,9 @@ void TabPrinter::toggle_options() toggle_option(el, !is_BBL_printer); } + if (m_active_page->title() == "Machine gcode") { + toggle_line("time_lapse_gcode", m_preset_bundle->printers.get_edited_preset().config.opt_enum("printer_structure") == PrinterStructure::psI3); + } wxString extruder_number; long val = 1; if ( m_active_page->title().IsSameAs("Extruder") ||