From c75c10e312b8d0bd5404d92db88c95a9e6186bc1 Mon Sep 17 00:00:00 2001 From: "zhimin.zeng" Date: Mon, 2 Sep 2024 17:59:57 +0800 Subject: [PATCH] ENH: Add gcode check for multi_extruder jira: none Change-Id: Iebc43e608c4509eb62b280af2d401fa9e0e089ba --- src/libslic3r/GCode.cpp | 14 +++++++ src/libslic3r/GCode/GCodeProcessor.cpp | 53 ++++++++++++++++++++++++++ src/libslic3r/GCode/GCodeProcessor.hpp | 17 +++++++++ src/libslic3r/Print.cpp | 10 +++++ src/libslic3r/Print.hpp | 3 +- src/slic3r/GUI/3DScene.cpp | 12 ++++++ src/slic3r/GUI/GCodeViewer.cpp | 2 + src/slic3r/GUI/GCodeViewer.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 22 +++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/PartPlate.cpp | 9 +++++ src/slic3r/GUI/PartPlate.hpp | 8 ++++ 12 files changed, 151 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index af0bbfb07..c5a1f7a52 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1305,6 +1305,20 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.result().filament_printable_reuslt = FilamentPrintableResult(conflict_filament, bed_type_to_gcode_string(m_config.curr_bed_type)); } m_processor.set_filaments(m_writer.extruders()); + // check gcode is valid in multi_extruder printabele area + int extruder_size = m_print->config().nozzle_diameter.values.size(); + if (extruder_size > 1) { + std::vector printable_area = m_print->get_printable_area(); + Polygon printable_poly = Polygon::new_scale(printable_area); + std::vector> extruder_printable_areas = m_print->get_extruder_printable_area(); + std::vector extruder_unprintable_polys; + for (const auto &e_printable_area : extruder_printable_areas) { + 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.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); diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 89558d881..039fe4ac8 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1070,6 +1070,59 @@ 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) +{ + m_result.gcode_check_result.reset(); + + auto to_2d = [](const Vec3d &pos) -> Point { + Point ps(scale_(pos.x()), scale_(pos.y())); + return ps; + }; + + std::map gcode_path_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[int(move.extruder_id)].emplace_back(to_2d(move.interpolation_points[i].cast())); + } + } + else { + gcode_path_pos[int(move.extruder_id)].emplace_back(to_2d(move.position.cast())); + } + } + } + + bool valid = true; + for (auto iter = gcode_path_pos.begin(); iter != gcode_path_pos.end(); ++iter) { + int extruder_id = filament_map[iter->first] - 1; + Polygon path_poly(iter->second); + BoundingBox bbox = path_poly.bounding_box(); + + // Simplified use bounding_box, Accurate calculation is not efficient + for (const Polygon &poly : unprintable_areas[extruder_id]) { + if (poly.bounding_box().overlap(bbox)) { + m_result.gcode_check_result.error_code = 1; + m_result.gcode_check_result.error_infos[extruder_id].push_back(iter->first); + valid = false; + } + } + + /* + // Accurate calculation is not efficient + for (const Polygon& poly : unprintable_areas[extruder_id]) { + if (poly.overlaps({path_poly})) { + m_result.gcode_check_result.error_code = 1; + valid = false; + break; + } + } + */ + } + + return valid; +} + void GCodeProcessor::apply_config(const PrintConfig& config) { m_parser.apply_config(config); diff --git a/src/libslic3r/GCode/GCodeProcessor.hpp b/src/libslic3r/GCode/GCodeProcessor.hpp index d0cac7d1a..f559b97db 100644 --- a/src/libslic3r/GCode/GCodeProcessor.hpp +++ b/src/libslic3r/GCode/GCodeProcessor.hpp @@ -132,9 +132,23 @@ namespace Slic3r { }; }; + using ConflictResultOpt = std::optional; + + struct GCodeCheckResult + { + int error_code = 0; // 0 means succeed + std::map> error_infos; // extruder_id to filament_ids + + void reset() { + error_code = 0; + error_infos.clear(); + } + }; + struct GCodeProcessorResult { ConflictResultOpt conflict_result; + GCodeCheckResult gcode_check_result; FilamentPrintableResult filament_printable_reuslt; struct SettingsIds @@ -256,6 +270,7 @@ namespace Slic3r { spiral_vase_layers = other.spiral_vase_layers; warnings = other.warnings; bed_type = other.bed_type; + gcode_check_result = other.gcode_check_result; filament_printable_reuslt = other.filament_printable_reuslt; #if ENABLE_GCODE_VIEWER_STATISTICS time = other.time; @@ -820,6 +835,8 @@ namespace Slic3r { public: 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); void apply_config(const PrintConfig& config); void set_filaments(const std::vector&filament_lists) { m_filament_lists=filament_lists;} diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 814dbea64..18d4ae6bd 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2370,6 +2370,16 @@ FilamentMapMode Print::get_filament_map_mode() const return m_config.filament_map_mode; } +std::vector Print::get_printable_area() +{ + return m_config.printable_area.values; +} + +std::vector> Print::get_extruder_printable_area() +{ + return m_config.extruder_printable_area.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 9a5956960..b889b91e1 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -827,7 +827,6 @@ public: // get the group label of filament size_t get_extruder_id(unsigned int filament_id) const; - // 1 based ids const std::vector>& get_unprintable_filament_ids() const { return m_unprintable_filament_ids; } void set_unprintable_filament_ids(const std::vector> &filament_ids) { m_unprintable_filament_ids = filament_ids; } @@ -954,6 +953,8 @@ private: FakeWipeTower m_fake_wipe_tower; bool m_has_auto_filament_map_result{false}; + std::vector> m_unprintable_filament_ids; + // OrcaSlicer: calibration Calib_Params m_calib_params; diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 631b56a22..7bc961f66 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1720,6 +1720,7 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo BuildVolume plate_build_volume(pp_bed_shape, build_volume.printable_height(), build_volume.extruder_areas()); const std::vector& exclude_areas = curr_plate->get_exclude_areas(); + curr_plate->clear_unprintable_filament_ids(); for (GLVolume* volume : this->volumes) { if (! volume->is_modifier && (volume->shader_outside_printer_detection_enabled || (! volume->is_wipe_tower && volume->composite_id.volume_id >= 0))) { @@ -1736,6 +1737,17 @@ bool GLVolumeCollection::check_outside_state(const BuildVolume &build_volume, Mo { std::vector inside_extruders; state = plate_build_volume.check_volume_bbox_state_with_extruder_areas(bb, inside_extruders); + if (state == BuildVolume::ObjectState::Limited) { + const ModelObjectPtrs &model_objects = model.objects; + ModelObject *model_object = model_objects[volume->object_idx()]; + ModelVolume *model_volume = model_object->volumes[volume->volume_idx()]; + for (size_t i = 0; i < inside_extruders.size(); ++i) { + if (!inside_extruders[i]) { + std::vector extruders = model_volume->get_extruders(); + curr_plate->append_unprintable_filament_ids(i, extruders); + } + } + } } break; case BuildVolume::Type::Circle: diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 1e1b33d4f..55a1b4033 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1147,6 +1147,8 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr m_conflict_result = gcode_result.conflict_result; if (m_conflict_result) { m_conflict_result.value().layer = m_layers.get_l_at(m_conflict_result.value()._height); } + m_gcode_check_result = gcode_result.gcode_check_result; + filament_printable_reuslt = gcode_result.filament_printable_reuslt; //BBS: add mutex for protection of gcode result gcode_result.unlock(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index afdf62eb1..f36d3f12c 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -746,6 +746,7 @@ public: //BBS ConflictResultOpt m_conflict_result; + GCodeCheckResult m_gcode_check_result; FilamentPrintableResult filament_printable_reuslt; private: diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a40e85135..2e2ebb825 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2901,6 +2901,7 @@ void GLCanvas3D::load_gcode_preview(const GCodeProcessorResult& gcode_result, co _set_warning_notification_if_needed(EWarning::ToolHeightOutside); _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::FilamentUnPrintableOnFirstLayer); } @@ -9548,6 +9549,8 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning) (m_gcode_viewer.get_max_print_height() - m_gcode_viewer.get_layers_zs()[max_z_layer] >= 1e-6); } 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; else if (warning == EWarning::FilamentUnPrintableOnFirstLayer) show = m_gcode_viewer.has_data() && m_gcode_viewer.filament_printable_reuslt.has_value(); } @@ -9616,6 +9619,25 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) case EWarning::ObjectOutside: text = _u8L("An object is layed over the boundary of plate."); break; case EWarning::ToolHeightOutside: text = _u8L("A G-code path goes beyond the max print height."); error = ErrorType::SLICING_ERROR; break; case EWarning::ToolpathOutside: text = _u8L("A G-code path goes beyond the boundary of plate."); error = ErrorType::SLICING_ERROR; break; + case EWarning::MultiExtruderPrintableError: { + text.clear(); + 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()) { + text += "\n"; + } + int extruder_id = error_iter->first + 1; + std::string filaments; + for (size_t i = 0; i < error_iter->second.size(); ++i) { + if (i > 0) { + filaments += ", "; + } + filaments += std::to_string(error_iter->second[i] + 1); + } + text += (boost::format(_u8L("Extruder %d conflicts with filaments: %s.")) %extruder_id %filaments).str(); + } + error = ErrorType::SLICING_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; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8f633915d..7570e03ee 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -379,6 +379,7 @@ class GLCanvas3D ObjectLimited, GCodeConflict, ToolHeightOutside, + MultiExtruderPrintableError, // after slice FilamentUnPrintableOnFirstLayer }; diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index e8997f071..76f6a83b9 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -2581,6 +2581,7 @@ void PartPlate::update_slice_context(BackgroundSlicingProcess & process) process.select_technology(this->printer_technology); process.set_current_plate(this); m_print->set_status_callback(statuscb); + m_print->set_unprintable_filament_ids(m_unprintable_filament_ids); process.switch_print_preprocess(); return; @@ -2940,6 +2941,14 @@ std::vector PartPlate::get_filament_maps() return filament_maps; } +void PartPlate::append_unprintable_filament_ids(int extruder_id, const std::vector &filament_ids) +{ + if (extruder_id > m_unprintable_filament_ids.size()) { + m_unprintable_filament_ids.resize(extruder_id + 1); + } + m_unprintable_filament_ids[extruder_id].insert(m_unprintable_filament_ids[extruder_id].end(), filament_ids.begin(), filament_ids.end()); +} + void PartPlate::set_filament_maps(const std::vector& f_maps) { std::vector& filament_maps = m_config.option("filament_map", true)->values; diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 940c63515..397797a94 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -111,6 +111,9 @@ private: std::vector slice_filaments_info; int m_print_index; + // filament ids of extruder + std::vector> m_unprintable_filament_ids; + std::string m_tmp_gcode_path; //use a temp path to store the gcode std::string m_temp_config_3mf_path; //use a temp path to store the config 3mf std::string m_gcode_path_from_3mf; //use a path to store the gcode loaded from 3mf @@ -490,6 +493,11 @@ public: std::vector get_filament_maps(); void set_filament_maps(const std::vector& f_maps); + const std::vector> &get_unprintable_filament_ids() const { return m_unprintable_filament_ids; } + void set_unprintable_filament_ids(const std::vector> &filament_ids) { m_unprintable_filament_ids = filament_ids; } + void clear_unprintable_filament_ids() { m_unprintable_filament_ids.clear(); } + void append_unprintable_filament_ids(int extruder_id, const std::vector &filament_ids); + void on_extruder_count_changed(int extruder_count); void set_filament_count(int filament_count); void on_filament_added();