diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 053d7d44c..838fb3f2a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1338,7 +1338,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu Polygons ploys = diff(printable_poly, Polygon::new_scale(e_printable_area)); extruder_unprintable_polys.emplace_back(ploys); } - m_processor.check_multi_extruder_gcode_valid(extruder_unprintable_polys, m_print->get_filament_maps()); + + m_processor.check_multi_extruder_gcode_valid(extruder_unprintable_polys, m_print->get_extruder_printable_height(), m_print->get_filament_maps()); } m_processor.finalize(true); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 54fbb147d..c3fbb3c15 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -152,6 +152,18 @@ static int get_object_label_id(const std::string_view comment_1) return id; } +static float get_z_height(const std::string_view comment_1) +{ + std::string comment(comment_1); + auto pos = comment.find(":"); + std::string num_str = comment.substr(pos + 1); + float print_z = 0.0f; + try { + print_z = stof(num_str); + } catch (const std::exception &) {} + return print_z; +} + void GCodeProcessor::CachedPosition::reset() { std::fill(position.begin(), position.end(), FLT_MAX); @@ -1358,7 +1370,7 @@ GCodeProcessor::GCodeProcessor() m_time_processor.machines[static_cast(PrintEstimatedStatistics::ETimeMode::Stealth)].line_m73_stop_mask = "M73 D%s\n"; } -bool GCodeProcessor::check_multi_extruder_gcode_valid(const std::vector &unprintable_areas, const std::vector &filament_map) +bool GCodeProcessor::check_multi_extruder_gcode_valid(const std::vector &unprintable_areas, const std::vector& printable_heights, const std::vector &filament_map) { m_result.limit_filament_maps.clear(); m_result.gcode_check_result.reset(); @@ -1370,18 +1382,23 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const std::vector> gcode_path_pos; // object_id, filament_id, pos + struct GCodePosInfo + { + Points pos; + float max_print_z; + }; + std::map> gcode_path_pos; // object_id, filament_id, pos for (const GCodeProcessorResult::MoveVertex &move : m_result.moves) { if (move.type == EMoveType::Extrude/* || move.type == EMoveType::Travel*/) { if (move.is_arc_move_with_interpolation_points()) { for (int i = 0; i < move.interpolation_points.size(); i++) { - gcode_path_pos[move.object_label_id][int(move.extruder_id)].emplace_back(to_2d(move.interpolation_points[i].cast())); + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.interpolation_points[i].cast())); } } else { - gcode_path_pos[move.object_label_id][int(move.extruder_id)].emplace_back(to_2d(move.position.cast())); + gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.position.cast())); } + gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z = std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z, move.print_z); } } @@ -1389,12 +1406,13 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const std::vectorfirst; - const std::map& path_pos = obj_iter->second; + const std::map &path_pos = obj_iter->second; for (auto iter = path_pos.begin(); iter != path_pos.end(); ++iter) { - int extruder_id = filament_map[iter->first] - 1; - Polygon path_poly(iter->second); + int extruder_id = filament_map[iter->first] - 1; + Polygon path_poly(iter->second.pos); BoundingBox bbox = path_poly.bounding_box(); + // check printable area // Simplified use bounding_box, Accurate calculation is not efficient for (Polygon poly : unprintable_areas[extruder_id]) { poly.translate(plate_offset); @@ -1403,11 +1421,22 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const std::vector filament_to_object_id; filament_to_object_id.first = iter->first; filament_to_object_id.second = object_label_id; - m_result.gcode_check_result.error_infos[extruder_id].push_back(filament_to_object_id); + m_result.gcode_check_result.print_area_error_infos[extruder_id].push_back(filament_to_object_id); valid = false; } } + // check printable height + if (iter->second.max_print_z > printable_heights[extruder_id]) { + m_result.gcode_check_result.error_code |= (1 << 1); + std::pair filament_to_object_id; + filament_to_object_id.first = iter->first; + filament_to_object_id.second = object_label_id; + m_result.gcode_check_result.print_height_error_infos[extruder_id].push_back(filament_to_object_id); + m_result.limit_filament_maps[iter->first] |= (1 << extruder_id); + valid = false; + } + for (int i = 0; i < unprintable_areas.size(); ++i) { for (Polygon poly : unprintable_areas[i]) { poly.translate(plate_offset); @@ -2714,6 +2743,12 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers return; } + // ; Z_HEIGHT: + if (boost::starts_with(comment, " Z_HEIGHT:")) { + m_print_z = get_z_height(comment); + return; + } + // wipe start tag if (boost::starts_with(comment, reserved_tag(ETags::Wipe_Start))) { m_wiping = true; @@ -5226,7 +5261,8 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type) Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[filament_id], Vec3f(m_arc_center(0, 0) + m_x_offset, m_arc_center(1, 0) + m_y_offset, m_arc_center(2, 0)) + m_extruder_offsets[filament_id], m_interpolation_points, - m_object_label_id + m_object_label_id, + m_print_z }); if (type == EMoveType::Seam) { diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index 3cc87f8cf..7a4d846bd 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -121,6 +121,18 @@ namespace Slic3r { using ConflictResultOpt = std::optional; + struct GCodeCheckResult + { + int error_code = 0; // 0 means succeed, 0001 printable area error, 0010 printable height error + std::map>> print_area_error_infos; // printable_area extruder_id to which cannot printed in this extruder + std::map>> print_height_error_infos; // printable_height extruder_id to which cannot printed in this extruder + void reset() { + error_code = 0; + print_area_error_infos.clear(); + print_height_error_infos.clear(); + } + }; + struct FilamentPrintableResult { std::vector conflict_filament; @@ -132,18 +144,6 @@ namespace Slic3r { }; }; - using ConflictResultOpt = std::optional; - - struct GCodeCheckResult - { - int error_code = 0; // 0 means succeed - std::map>> error_infos; // extruder_id to which cannot printed in this extruder - void reset() { - error_code = 0; - error_infos.clear(); - } - }; - struct GCodeProcessorResult { struct FilamentSequenceHash @@ -197,6 +197,7 @@ namespace Slic3r { Vec3f arc_center_position{ Vec3f::Zero() }; // mm std::vector interpolation_points; // interpolation points of arc for drawing int object_label_id{-1}; + float print_z{0.0f}; float volumetric_rate() const { return feedrate * mm3_per_mm; } //BBS: new function to support arc move @@ -763,6 +764,7 @@ namespace Slic3r { bool m_virtual_flushing; // mark a section with virtual flush, only for statistics bool m_wipe_tower; int m_object_label_id{-1}; + float m_print_z{0.0f}; std::vector m_remaining_volume; std::vector m_filament_lists; std::vector m_filament_nozzle_temp; @@ -845,7 +847,7 @@ namespace Slic3r { GCodeProcessor(); // check whether the gcode path meets the filament_map grouping requirements - bool check_multi_extruder_gcode_valid(const std::vector &unprintable_areas, const std::vector& filament_map); + bool check_multi_extruder_gcode_valid(const std::vector &unprintable_areas, const std::vector& printable_heights, const std::vector& filament_map); void apply_config(const PrintConfig& config); void set_filaments(const std::vector&filament_lists) { m_filament_lists=filament_lists;} diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 5c5db838a..31fe45dbc 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -945,7 +945,7 @@ static std::vector s_Preset_printer_options { "printable_area", "extruder_printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", "single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode","printing_by_object_gcode","before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "printer_model", "printer_variant", "printer_extruder_id", "printer_extruder_variant", "extruder_variant_list", "default_nozzle_volume_type", - "printable_height", "extruder_clearance_radius", "extruder_clearance_max_radius","extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", + "printable_height", "extruder_printable_height", "extruder_clearance_dist_to_rod", "extruder_clearance_max_radius","extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod", "nozzle_height", "default_print_profile", "inherits", "silent_mode", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 52445d31a..97ca0d965 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2399,6 +2399,11 @@ std::vector> Print::get_extruder_printable_area() return m_config.extruder_printable_area.values; } +std::vector Print::get_extruder_printable_height() +{ + return m_config.extruder_printable_height.values; +} + size_t Print::get_extruder_id(unsigned int filament_id) const { std::vector filament_map = get_filament_maps(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9e802d573..cc3c3b265 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -836,6 +836,7 @@ public: std::vector get_printable_area(); std::vector> get_extruder_printable_area(); + std::vector get_extruder_printable_height(); bool enable_timelapse_print() const; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 04d750d5a..d635dfe82 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -542,6 +542,15 @@ void PrintConfigDef::init_common_params() def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(100.0)); + def = this->add("extruder_printable_height", coFloats); + def->label = L("Printable height"); + def->tooltip = L("Maximum printable height which is limited by mechanism of printer"); + def->sidetext = L("mm"); + def->min = 0; + def->max = 1000; + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloatsNullable{100.0, 200.0}); + // Options used by physical printers def = this->add("preset_names", coStrings); @@ -5278,6 +5287,7 @@ std::set printer_extruder_options = { "nozzle_diameter", "default_nozzle_volume_type", "extruder_printable_area", + "extruder_printable_height", "min_layer_height", "max_layer_height" }; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1252e5b89..b76a8089b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1140,6 +1140,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloatsNullable, min_layer_height)) ((ConfigOptionString, printer_notes)) ((ConfigOptionFloat, printable_height)) + ((ConfigOptionFloatsNullable, extruder_printable_height)) ((ConfigOptionPoint, best_object_pos)) ((ConfigOptionFloats, slow_down_min_speed)) ((ConfigOptionFloatsNullable, nozzle_diameter)) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0307786d7..a4f13984a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2959,6 +2959,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co _set_warning_notification_if_needed(EWarning::ToolpathOutside); _set_warning_notification_if_needed(EWarning::GCodeConflict); _set_warning_notification_if_needed(EWarning::MultiExtruderPrintableError); + _set_warning_notification_if_needed(EWarning::MultiExtruderHeightOutside); _set_warning_notification_if_needed(EWarning::FilamentUnPrintableOnFirstLayer); } @@ -9607,7 +9608,9 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) } else if (warning == EWarning::GCodeConflict) show = m_gcode_viewer.has_data() && m_gcode_viewer.is_contained_in_bed() && m_gcode_viewer.m_conflict_result.has_value(); else if (warning == EWarning::MultiExtruderPrintableError) - show = m_gcode_viewer.has_data() && m_gcode_viewer.m_gcode_check_result.error_code != 0; + show = m_gcode_viewer.has_data() && (m_gcode_viewer.m_gcode_check_result.error_code & 1); + else if (warning == EWarning::MultiExtruderHeightOutside) + show = m_gcode_viewer.has_data() && (m_gcode_viewer.m_gcode_check_result.error_code & (1 << 1)); else if (warning == EWarning::FilamentUnPrintableOnFirstLayer) show = m_gcode_viewer.has_data() && m_gcode_viewer.filament_printable_reuslt.has_value(); } @@ -9646,7 +9649,8 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) PLATER_ERROR, SLICING_SERIOUS_WARNING, SLICING_ERROR, - SLICING_LIMIT_ERROR + SLICING_LIMIT_ERROR, + SLICING_HEIGHT_OUTSIDE }; std::string text; ErrorType error = ErrorType::PLATER_WARNING; @@ -9682,8 +9686,8 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) break; } case EWarning::MultiExtruderPrintableError: { - for (auto error_iter = m_gcode_viewer.m_gcode_check_result.error_infos.begin(); error_iter != m_gcode_viewer.m_gcode_check_result.error_infos.end(); ++error_iter) { - if (error_iter != m_gcode_viewer.m_gcode_check_result.error_infos.begin()) { + for (auto error_iter = m_gcode_viewer.m_gcode_check_result.print_area_error_infos.begin(); error_iter != m_gcode_viewer.m_gcode_check_result.print_area_error_infos.end(); ++error_iter) { + if (error_iter != m_gcode_viewer.m_gcode_check_result.print_area_error_infos.begin()) { text += "\n"; } int extruder_id = error_iter->first + 1; // change extruder id to 1 based @@ -9696,7 +9700,6 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) int filament_id = error_iter->second[i].first + 1; // change filament id to 1 based int object_label_id = error_iter->second[i].second; - //ModelObject* object->instances[0]->get_labeled_id(); filaments += std::to_string(filament_id); for (int object_idx = 0; object_idx < (int) m_model->objects.size(); ++object_idx) { const ModelObject *model_object = m_model->objects[object_idx]; @@ -9732,6 +9735,63 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) error = ErrorType::SLICING_LIMIT_ERROR; break; } + case EWarning::MultiExtruderHeightOutside: { + for (auto error_iter = m_gcode_viewer.m_gcode_check_result.print_height_error_infos.begin(); error_iter != m_gcode_viewer.m_gcode_check_result.print_height_error_infos.end(); ++error_iter) { + if (error_iter != m_gcode_viewer.m_gcode_check_result.print_height_error_infos.begin()) { + text += "\n"; + } + int extruder_id = error_iter->first + 1; // change extruder id to 1 based + std::set filament_ids; + std::vector slice_error_object_idxs; + for (size_t i = 0; i < error_iter->second.size(); ++i) { + int filament_id = error_iter->second[i].first + 1; // change filament id to 1 based + int object_label_id = error_iter->second[i].second; + filament_ids.insert(filament_id); + for (int object_idx = 0; object_idx < (int) m_model->objects.size(); ++object_idx) { + const ModelObject *model_object = m_model->objects[object_idx]; + for (int instance_idx = 0; instance_idx < (int) model_object->instances.size(); ++instance_idx) { + const ModelInstance *model_instance = model_object->instances[instance_idx]; + auto expect_id = model_instance->get_labeled_id(); + if (object_label_id == expect_id) { + slice_error_object_idxs.emplace_back(object_idx); + } + } + } + } + + std::string filaments; + int index = 0; + for (auto filament_id : filament_ids) { + if (index == 0) { + filaments += ", "; + } + filaments += std::to_string(filament_id); + ++index; + } + + for (GLVolume *volume : m_gcode_viewer.m_shells.volumes.volumes) { + for (auto obj_idx : slice_error_object_idxs) { + if (volume->object_idx() == obj_idx) { + volume->slice_error = true; + volume->selected = true; + } + } + } + std::string extruder_name = extruder_id == 1 ? "Left extruder" : "Right extruder"; + if (error_iter->second.size() == 1) { + text += (boost::format(_u8L("Filament %d is placed in the %s, but the generated G-code path exceeds the printable height of the %s.")) % filaments % extruder_name % extruder_name).str(); + } else { + text += (boost::format(_u8L("Filaments %d is placed in the %s, but the generated G-code path exceeds the printable height of the %s.")) % filaments % extruder_name % extruder_name).str(); + } + } + if (!text.empty()) { + text += "\n"; + text += _u8L("Open wiki for more information."); + } + error = ErrorType::SLICING_LIMIT_ERROR; + break; + } + // BBS: remove _u8L() for SLA case EWarning::SlaSupportsOutside: text = ("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break; case EWarning::SomethingNotShown: text = _u8L("Only the object being edited is visible."); break; @@ -9798,9 +9858,15 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) break; case SLICING_LIMIT_ERROR: if (state) - notification_manager.push_slicing_limit_error_notification(text); + notification_manager.push_slicing_customize_error_notification(NotificationType::BBLSliceLimitError, NotificationManager::NotificationLevel::ErrorNotificationLevel, text); else - notification_manager.close_slicing_limit_error_notification(text); + notification_manager.close_slicing_customize_error_notification(NotificationType::BBLSliceLimitError, NotificationManager::NotificationLevel::ErrorNotificationLevel); + break; + case SLICING_HEIGHT_OUTSIDE: + if (state) + notification_manager.push_slicing_customize_error_notification(NotificationType::BBLSliceMultiExtruderHeightOutside, NotificationManager::NotificationLevel::ErrorNotificationLevel, text); + else + notification_manager.close_slicing_customize_error_notification(NotificationType::BBLSliceMultiExtruderHeightOutside, NotificationManager::NotificationLevel::ErrorNotificationLevel); break; default: break; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 511a68e37..4c211e3cb 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -381,6 +381,7 @@ class GLCanvas3D ToolHeightOutside, TPUPrintableError, MultiExtruderPrintableError, // after slice + MultiExtruderHeightOutside, // after slice FilamentUnPrintableOnFirstLayer }; diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index 1f08785d6..dcb109647 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1745,17 +1745,17 @@ void NotificationManager::close_general_error_notification(const std::string& te } } -void NotificationManager::push_slicing_limit_error_notification(const std::string &text) +void NotificationManager::push_slicing_customize_error_notification(NotificationType type, NotificationLevel level, const std::string &text) { set_all_slicing_errors_gray(false); - push_notification_data({NotificationType::BBLSliceLimitError, NotificationLevel::ErrorNotificationLevel, 0, _u8L("Error:") + "\n" + text}, 0); + push_notification_data({type, level, 0, _u8L("Error:") + "\n" + text}, 0); set_slicing_progress_hidden(); } -void NotificationManager::close_slicing_limit_error_notification(const std::string &text) +void NotificationManager::close_slicing_customize_error_notification(NotificationType type, NotificationLevel level) { for (std::unique_ptr ¬ification : m_pop_notifications) { - if (notification->get_type() == NotificationType::BBLSliceLimitError) { + if (notification->get_type() == type && notification->get_data().level == level) { notification->close(); } } diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 751e2c266..ae1a94c64 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -234,8 +234,8 @@ public: void close_plater_warning_notification(const std::string& text); // GCode exceeds the printing range of the extruder - void push_slicing_limit_error_notification(const std::string &text); - void close_slicing_limit_error_notification(const std::string &text); + void push_slicing_customize_error_notification(NotificationType type, NotificationLevel level, const std::string &text); + void close_slicing_customize_error_notification(NotificationType type, NotificationLevel level); // Object warning with ObjectID, closes when object is deleted. ID used is of object not print like in slicing warning. void push_simplify_suggestion_notification(const std::string& text, ObjectID object_id, const std::string& hypertext = "", diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 91a69d544..f0b0050a2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3151,7 +3151,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "nozzle_height", "skirt_loops", "skirt_distance", "brim_width", "brim_object_gap", "brim_type", "nozzle_diameter", "single_extruder_multi_material", "enable_prime_tower", "wipe_tower_x", "wipe_tower_y", "prime_tower_width", "prime_tower_brim_width", "prime_volume", - "extruder_colour", "filament_colour", "material_colour", "printable_height", "printer_model", "printer_technology", + "extruder_colour", "filament_colour", "material_colour", "printable_height", "extruder_printable_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. "layer_height", "initial_layer_print_height", "min_layer_height", "max_layer_height", "brim_width", "wall_loops", "wall_filament", "sparse_infill_density", "sparse_infill_filament", "top_shell_layers",