ENH: add wrapping detection

jira: STUDIO-13192
Change-Id: I0fb5692b18cdb3b7af624a831dcfb0f635b165f0
(cherry picked from commit 89a8b6ea5e34340cba3750416ce3100dcc39f5c2)
(cherry picked from commit 50bc8c10a7b4ddc0b2dfe9cbab438b9d307b7fc0)
This commit is contained in:
zhimin.zeng
2025-07-05 14:52:21 +08:00
committed by Noisyfox
parent 070a8e62a1
commit 1ccdf7b43b
31 changed files with 323 additions and 50 deletions

View File

@@ -5639,7 +5639,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
upgrade_display_hold_count = HOLD_COUNT_MAX;
BOOST_LOG_TRIVIAL(info) << "ack of upgrade_confirm";
}
if (j["upgrade"].contains("err_code")) {
if (j["upgrade"]["err_code"].is_number()) {
add_command_error_code_dlg(j["upgrade"]["err_code"].get<int>());
@@ -7110,7 +7110,7 @@ void MachineObject::add_command_error_code_dlg(int command_err)
if (!token.expired()) { m_command_error_code_dlgs.erase((GUI::DeviceErrorDialog*)event.GetEventObject());}
event.Skip();
});
device_error_dialog->show_error_code(command_err);
m_command_error_code_dlgs.insert(device_error_dialog);
});
@@ -7912,6 +7912,11 @@ std::string DeviceManager::get_printer_ext_img(std::string type_str, int pos) {
return std::string();
}
bool DeviceManager::support_wrapping_detection(const std::string &type_str)
{
return get_value_from_config<bool>(type_str, "support_wrapping_deteciton");
}
bool DeviceManager::get_printer_is_enclosed(std::string type_str) {
return get_value_from_config<bool>(type_str, "printer_is_enclosed"); }

View File

@@ -1603,6 +1603,7 @@ public:
static bool get_printer_is_enclosed(std::string type_str);
static bool get_printer_can_set_nozzle(std::string type_str);// can set nozzle from studio
static bool load_filaments_blacklist_config();
static bool support_wrapping_detection(const std::string& type_str);
static string get_fan_text(const std::string& type_str, const std::string& key);

View File

@@ -2908,9 +2908,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
const DynamicPrintConfig &dconfig = wxGetApp().preset_bundle->prints.get_edited_preset().config;
auto timelapse_type = dconfig.option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
bool timelapse_enabled = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false;
bool need_wipe_tower = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false;
if (wt && (timelapse_enabled || filaments_count > 1) && !wxGetApp().plater()->only_gcode_mode() && !wxGetApp().plater()->is_gcode_3mf()) {
const DynamicPrintConfig & printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
if (printer_config.has("enable_wrapping_detection")) {
need_wipe_tower |= dynamic_cast<const ConfigOptionBool*>(printer_config.option("enable_wrapping_detection"))->value;
}
if (wt && (need_wipe_tower || filaments_count > 1) && !wxGetApp().plater()->only_gcode_mode() && !wxGetApp().plater()->is_gcode_3mf()) {
for (int plate_id = 0; plate_id < n_plates; plate_id++) {
// If print ByObject and there is only one object in the plate, the wipe tower is allowed to be generated.
PartPlate* part_plate = ppl.get_plate(plate_id);
@@ -2931,14 +2936,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
const Print* print = m_process->fff_print();
const Print* current_print = part_plate->fff_print();
if (!timelapse_enabled && part_plate->get_extruders(true).size() < 2) continue;
if (!need_wipe_tower && part_plate->get_extruders(true).size() < 2) continue;
if (part_plate->get_objects_on_this_plate().empty()) continue;
float brim_width = print->wipe_tower_data(filaments_count).brim_width;
const DynamicPrintConfig &print_cfg = wxGetApp().preset_bundle->prints.get_edited_preset().config;
double wipe_vol = get_max_element(v);
int nozzle_nums = wxGetApp().preset_bundle->get_printer_extruder_count();
Vec3d wipe_tower_size = ppl.get_plate(plate_id)->estimate_wipe_tower_size(print_cfg, w, wipe_vol, nozzle_nums);
Vec3d wipe_tower_size = ppl.get_plate(plate_id)->estimate_wipe_tower_size(print_cfg, w, wipe_vol, nozzle_nums, 0, false, dynamic_cast<const ConfigOptionBool*>(printer_config.option("enable_wrapping_detection"))->value);
{
const float margin = WIPE_TOWER_MARGIN + brim_width;

View File

@@ -1094,6 +1094,8 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
if (opt_key == "printable_area")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else if (opt_key == "bed_exclude_area")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else if (opt_key == "wrapping_detection_path")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
@@ -1230,6 +1232,8 @@ boost::any ConfigOptionsGroup::get_config_value2(const DynamicPrintConfig& confi
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else if (opt_key == "bed_exclude_area")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else if (opt_key == "wrapping_detection_path")
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
else
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
break;

View File

@@ -1905,7 +1905,7 @@ bool PartPlate::check_compatible_of_nozzle_and_filament(const DynamicPrintConfig
return wipe_tower_size;
}*/
Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count, int plate_extruder_size, bool use_global_objects) const
Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count, int plate_extruder_size, bool use_global_objects, bool enable_wrapping_detection) const
{
Vec3d wipe_tower_size;
double layer_height = 0.08f; // hard code layer height
@@ -1935,7 +1935,7 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con
wipe_tower_size(2) = max_height;
//const DynamicPrintConfig &dconfig = wxGetApp().preset_bundle->prints.get_edited_preset().config;
auto timelapse_type = config.option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
bool timelapse_enabled = timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false;
bool need_wipe_tower = (timelapse_type ? (timelapse_type->value == TimelapseType::tlSmooth) : false) | enable_wrapping_detection;
double extra_spacing = config.option("prime_tower_infill_gap")->getFloat() / 100.;
const ConfigOptionBool* use_rib_wall_opt = config.option<ConfigOptionBool>("prime_tower_rib_wall");
bool use_rib_wall = use_rib_wall_opt ? use_rib_wall_opt->value: true;
@@ -1958,7 +1958,7 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con
if (extruder_count == 2) volume += filament_change_volume * (int) (plate_extruder_size / 2);
if (use_rib_wall) {
depth = std::sqrt(volume / layer_height * extra_spacing);
if (timelapse_enabled || plate_extruder_size > 1) {
if (need_wipe_tower || plate_extruder_size > 1) {
float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height);
depth = std::max((double) min_wipe_tower_depth, depth);
depth += rib_width / std::sqrt(2) + m_print->config().prime_tower_extra_rib_length.value;
@@ -1967,7 +1967,7 @@ Vec3d PartPlate::estimate_wipe_tower_size(const DynamicPrintConfig & config, con
}
else {
depth = volume/ (layer_height * w) *extra_spacing;
if (timelapse_enabled || depth > EPSILON) {
if (need_wipe_tower || depth > EPSILON) {
float min_wipe_tower_depth = WipeTower::get_limit_depth_by_height(max_height);
depth = std::max((double)min_wipe_tower_depth, depth);
}
@@ -1986,7 +1986,9 @@ arrangement::ArrangePolygon PartPlate::estimate_wipe_tower_polygon(const Dynamic
//float a = dynamic_cast<const ConfigOptionFloat*>(config.option("wipe_tower_rotation_angle"))->value;
std::vector<double> v = dynamic_cast<const ConfigOptionFloats*>(config.option("filament_prime_volume"))->values;
float tower_brim_width = dynamic_cast<const ConfigOptionFloat*>(config.option("prime_tower_brim_width"))->value;
wt_size = estimate_wipe_tower_size(config, w, get_max_element(v), extruder_count, plate_extruder_size, use_global_objects);
const ConfigOptionBool * wrapping_opt = dynamic_cast<const ConfigOptionBool *>(config.option("enable_wrapping_detection"));
bool enable_wrapping = (wrapping_opt != nullptr) && wrapping_opt->value;
wt_size = estimate_wipe_tower_size(config, w, get_max_element(v), extruder_count, plate_extruder_size, use_global_objects, enable_wrapping);
int plate_width=m_width, plate_depth=m_depth;
float depth = wt_size(1);
float margin = WIPE_TOWER_MARGIN + tower_brim_width, wp_brim_width = 0.f;

View File

@@ -322,7 +322,7 @@ public:
Vec3d get_origin() { return m_origin; }
//Vec3d calculate_wipe_tower_size(const DynamicPrintConfig &config, const double w, const double wipe_volume, int plate_extruder_size = 0, bool use_global_objects = false) const;
Vec3d estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count = 1, int plate_extruder_size = 0, bool use_global_objects = false) const;
Vec3d estimate_wipe_tower_size(const DynamicPrintConfig & config, const double w, const double wipe_volume, int extruder_count = 1, int plate_extruder_size = 0, bool use_global_objects = false, bool enable_wrapping_detection = false) const;
arrangement::ArrangePolygon estimate_wipe_tower_polygon(const DynamicPrintConfig & config, int plate_index, Vec3d& wt_pos, Vec3d& wt_size, int extruder_count = 1, int plate_extruder_size = 0, bool use_global_objects = false) const;
bool check_objects_empty_and_gcode3mf(std::vector<int> &result) const;
// get used filaments from config, 1 based idx

View File

@@ -956,7 +956,8 @@ void Tab::init_options_list()
for (const std::string& opt_key : m_config->keys())
{
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails") {
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails"
|| opt_key == "wrapping_detection_path") {
m_options_list.emplace(opt_key, m_opt_status_value);
continue;
}
@@ -1569,13 +1570,31 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
if (opt_key == "enable_prime_tower") {
auto timelapse_type = m_config->option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
bool timelapse_enabled = timelapse_type->value == TimelapseType::tlSmooth;
if (!boost::any_cast<bool>(value) && timelapse_enabled) {
MessageDialog dlg(wxGetApp().plater(), _L("A prime tower is required for smooth timelapse. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"),
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
if (dlg.ShowModal() == wxID_NO) {
DynamicPrintConfig new_conf = *m_config;
new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true));
m_config_manipulation.apply(m_config, &new_conf);
if (!boost::any_cast<bool>(value)) {
bool set_enable_prime_tower = false;
if (timelapse_enabled) {
MessageDialog
dlg(wxGetApp().plater(),
_L("A prime tower is required for smooth timelapse. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"),
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
if (dlg.ShowModal() == wxID_NO) {
DynamicPrintConfig new_conf = *m_config;
new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true));
m_config_manipulation.apply(m_config, &new_conf);
set_enable_prime_tower = true;
}
}
bool enable_wrapping = m_preset_bundle->printers.get_edited_preset().config.option<ConfigOptionBool>("enable_wrapping_detection")->value;
if (enable_wrapping && !set_enable_prime_tower) {
MessageDialog dlg(wxGetApp().plater(),
_L("A prime tower is required for wrapping detection. There may be flaws on the model without prime tower. Are you sure you want to disable prime tower?"),
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
if (dlg.ShowModal() == wxID_NO) {
DynamicPrintConfig new_conf = *m_config;
new_conf.set_key_value("enable_prime_tower", new ConfigOptionBool(true));
m_config_manipulation.apply(m_config, &new_conf);
set_enable_prime_tower = true;
}
}
wxGetApp().plater()->update();
}
@@ -1593,6 +1612,23 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
update_wiping_button_visibility();
}
if (opt_key == "enable_wrapping_detection") {
bool wipe_tower_enabled = m_preset_bundle->prints.get_edited_preset().config.option<ConfigOptionBool>("enable_prime_tower")->value;
if (boost::any_cast<bool>(value) && !wipe_tower_enabled) {
MessageDialog dlg(wxGetApp().plater(),
_L("Prime tower is required for wrapping detection. There may be flaws on the model without prime tower. Do you still want to enable wrapping detection?"),
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
if (dlg.ShowModal() == wxID_NO) {
DynamicPrintConfig new_conf = *m_config;
new_conf.set_key_value("enable_wrapping_detection", new ConfigOptionBool(false));
m_config_manipulation.apply(m_config, &new_conf);
wxGetApp().plater()->update();
}
} else {
wxGetApp().plater()->update();
}
}
if (opt_key == "precise_z_height") {
bool wipe_tower_enabled = m_config->option<ConfigOptionBool>("enable_prime_tower")->value;
if (boost::any_cast<bool>(value) && wipe_tower_enabled) {
@@ -4162,6 +4198,9 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line("pellet_modded_printer", "pellet-flow-coefficient");
optgroup->append_single_option_line("bbl_use_printhost");
optgroup->append_single_option_line("scan_first_layer");
optgroup->append_single_option_line("enable_wrapping_detection");
optgroup->append_single_option_line("wrapping_detection_layers");
//optgroup->append_single_option_line("wrapping_detection_path");
optgroup->append_single_option_line("disable_m73");
option = optgroup->get_option("thumbnails");
option.opt.full_width = true;
@@ -4306,6 +4345,17 @@ void TabPrinter::build_fff()
option.opt.height = gcode_field_height;//150;
optgroup->append_single_option_line(option);
optgroup = page->new_optgroup(L("Wrapping Detection 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);
};
optgroup->edit_custom_gcode = edit_custom_gcode_fn;
option = optgroup->get_option("wrapping_detection_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_title = optgroup->title](const t_config_option_key& opt_key, const boost::any& value) {
validate_custom_gcode_cb(this, optgroup_title, opt_key, value);
@@ -4975,6 +5025,16 @@ void TabPrinter::toggle_options()
// SoftFever: hide non-BBL settings
for (auto el : {"use_firmware_retraction", "use_relative_e_distances", "support_multi_bed_types", "pellet_modded_printer", "bed_mesh_max", "bed_mesh_min", "bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "thumbnails"})
toggle_line(el, !is_BBL_printer);
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
std::string printer_type = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
toggle_line("enable_wrapping_detection", DeviceManager::support_wrapping_detection(printer_type));
}
if (m_active_page->title() == L("Machine G-code")) {
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
std::string printer_type = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
toggle_line("wrapping_detection_gcode", DeviceManager::support_wrapping_detection(printer_type));
}
if (m_active_page->title() == L("Multimaterial")) {

View File

@@ -1363,6 +1363,9 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
else if (opt_key == "head_wrap_detect_zone") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
}
else if (opt_key == "wrapping_detection_path") {
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
}
Vec2d val = config.opt<ConfigOptionPoints>(opt_key)->get_at(opt_idx);
return from_u8((boost::format("[%1%]") % ConfigOptionPoint(val).serialize()).str());
}