mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 13:16:00 +08:00
SPE-2491: Basic support for printing with different nozzle diameters on tool-changer printers.
Add an option that automatically changes all extrusion widths to zero, which will automatically calculate extrusion width based on nozzle diameters. Allow using wipe tower with different nozzle diameters and show just warning about it. Add an option for infill combination that automatically calculates the number of layers based on nozzle diameter.
This commit is contained in:
parent
66cf372def
commit
a718a7606a
@ -510,6 +510,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"perimeter_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
||||
"wall_distribution_count", "min_feature_size", "min_bead_width",
|
||||
"top_one_perimeter_type", "only_one_perimeter_first_layer",
|
||||
"automatic_extrusion_widths", "automatic_infill_combination", "automatic_infill_combination_max_layer_height",
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_filament_options {
|
||||
|
@ -283,6 +283,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (opt_key == "avoid_crossing_curled_overhangs") {
|
||||
osteps.emplace_back(posEstimateCurledExtrusions);
|
||||
} else if (opt_key == "automatic_extrusion_widths") {
|
||||
osteps.emplace_back(posPerimeters);
|
||||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
//FIXME invalidate all steps of all objects as well?
|
||||
@ -627,15 +629,19 @@ std::string Print::validate(std::vector<std::string>* warnings) const
|
||||
if (this->has_wipe_tower() && ! m_objects.empty()) {
|
||||
// Make sure all extruders use same diameter filament and have the same nozzle diameter
|
||||
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
|
||||
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front());
|
||||
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front());
|
||||
double first_filament_diam = m_config.filament_diameter.get_at(extruders.front());
|
||||
|
||||
bool allow_nozzle_diameter_differ_warning = (warnings != nullptr);
|
||||
for (const auto& extruder_idx : extruders) {
|
||||
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
|
||||
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
|
||||
double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
|
||||
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|
||||
|| std::abs((filament_diam-first_filament_diam)/first_filament_diam) > 0.1)
|
||||
return _u8L("The wipe tower is only supported if all extruders have the same nozzle diameter "
|
||||
"and use filaments of the same diameter.");
|
||||
if (allow_nozzle_diameter_differ_warning && (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam)) {
|
||||
allow_nozzle_diameter_differ_warning = false;
|
||||
warnings->emplace_back("_WIPE_TOWER_NOZZLE_DIAMETER_DIFFER");
|
||||
} else if (std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) {
|
||||
return _u8L("The wipe tower is only supported if all extruders use filaments of the same diameter.");
|
||||
}
|
||||
}
|
||||
|
||||
if (m_config.gcode_flavor != gcfRepRapSprinter && m_config.gcode_flavor != gcfRepRapFirmware &&
|
||||
|
@ -527,6 +527,31 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnum<ArcFittingType>(ArcFittingType::Disabled));
|
||||
|
||||
def = this->add("automatic_extrusion_widths", coBool);
|
||||
def->label = L("Automatic extrusion widths calculation");
|
||||
def->category = L("Extrusion Width");
|
||||
def->tooltip = L("Automatically calculates extrusion widths based on the nozzle diameter of the currently used extruder. "
|
||||
"This setting is essential for printing with different nozzle diameters.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("automatic_infill_combination", coBool);
|
||||
def->label = L("Automatic infill combination");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("This feature automatically combine infill of several layers and speeds up your print by extruding thicker "
|
||||
"infill layers while preserving thin perimeters, thus accuracy.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("automatic_infill_combination_max_layer_height", coFloatOrPercent);
|
||||
def->label = L("Automatic infill combination - Max layer height");
|
||||
def->category = L("Infill");
|
||||
def->tooltip = L("Maximum layer height for combining infill when automatic infill combining is enabled. "
|
||||
"Maximum layer height could be specified either as an absolute in millimeters value or as a percentage of nozzle diameter. "
|
||||
"For printing with different nozzle diameters, it is recommended to use percentage value over absolute value.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(100., true));
|
||||
|
||||
// Maximum extruder temperature, bumped to 1500 to support printing of glass.
|
||||
const int max_temp = 1500;
|
||||
def = this->add("avoid_crossing_curled_overhangs", coBool);
|
||||
|
@ -685,6 +685,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
PrintRegionConfig,
|
||||
|
||||
((ConfigOptionBool, automatic_infill_combination))
|
||||
((ConfigOptionFloatOrPercent, automatic_infill_combination_max_layer_height))
|
||||
((ConfigOptionFloat, bridge_angle))
|
||||
((ConfigOptionInt, bottom_solid_layers))
|
||||
((ConfigOptionFloat, bottom_solid_min_thickness))
|
||||
@ -898,6 +900,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
PrintConfig,
|
||||
(MachineEnvelopeConfig, GCodeConfig),
|
||||
|
||||
((ConfigOptionBool, automatic_extrusion_widths))
|
||||
((ConfigOptionBool, avoid_crossing_curled_overhangs))
|
||||
((ConfigOptionBool, avoid_crossing_perimeters))
|
||||
((ConfigOptionFloatOrPercent, avoid_crossing_perimeters_max_detour))
|
||||
|
@ -808,6 +808,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
opt_key == "interface_shells"
|
||||
|| opt_key == "infill_only_where_needed"
|
||||
|| opt_key == "infill_every_layers"
|
||||
|| opt_key == "automatic_infill_combination"
|
||||
|| opt_key == "automatic_infill_combination_max_layer_height"
|
||||
|| opt_key == "solid_infill_every_layers"
|
||||
|| opt_key == "ensure_vertical_shell_thickness"
|
||||
|| opt_key == "bottom_solid_min_thickness"
|
||||
@ -3053,16 +3055,24 @@ void PrintObject::discover_horizontal_shells()
|
||||
void PrintObject::combine_infill()
|
||||
{
|
||||
// Work on each region separately.
|
||||
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
|
||||
const PrintRegion ®ion = this->printing_region(region_id);
|
||||
const size_t every = region.config().infill_every_layers.value;
|
||||
if (every < 2 || region.config().fill_density == 0.)
|
||||
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
|
||||
const PrintRegion ®ion = this->printing_region(region_id);
|
||||
const size_t combine_infill_every_n_layers = region.config().infill_every_layers.value;
|
||||
const bool automatic_infill_combination = region.config().automatic_infill_combination;
|
||||
const bool enable_combine_infill = automatic_infill_combination || combine_infill_every_n_layers >= 2;
|
||||
|
||||
if (!enable_combine_infill || region.config().fill_density == 0.) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Limit the number of combined layers to the maximum height allowed by this regions' nozzle.
|
||||
//FIXME limit the layer height to max_layer_height
|
||||
double nozzle_diameter = std::min(
|
||||
this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1),
|
||||
this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1));
|
||||
const double nozzle_diameter = std::min(this->print()->config().nozzle_diameter.get_at(region.config().infill_extruder.value - 1),
|
||||
this->print()->config().nozzle_diameter.get_at(region.config().solid_infill_extruder.value - 1));
|
||||
|
||||
const double automatic_infill_combination_max_layer_height = region.config().automatic_infill_combination_max_layer_height.get_abs_value(nozzle_diameter);
|
||||
const double max_combine_layer_height = automatic_infill_combination ? std::min(automatic_infill_combination_max_layer_height, nozzle_diameter) : nozzle_diameter;
|
||||
|
||||
// define the combinations
|
||||
std::vector<size_t> combine(m_layers.size(), 0);
|
||||
{
|
||||
@ -3070,19 +3080,21 @@ void PrintObject::combine_infill()
|
||||
size_t num_layers = 0;
|
||||
for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
const Layer *layer = m_layers[layer_idx];
|
||||
if (layer->id() == 0)
|
||||
const Layer &layer = *m_layers[layer_idx];
|
||||
if (layer.id() == 0)
|
||||
// Skip first print layer (which may not be first layer in array because of raft).
|
||||
continue;
|
||||
|
||||
// Check whether the combination of this layer with the lower layers' buffer
|
||||
// would exceed max layer height or max combined layer count.
|
||||
if (current_height + layer->height >= nozzle_diameter + EPSILON || num_layers >= every) {
|
||||
if (current_height + layer.height >= max_combine_layer_height + EPSILON || (!automatic_infill_combination && num_layers >= combine_infill_every_n_layers)) {
|
||||
// Append combination to lower layer.
|
||||
combine[layer_idx - 1] = num_layers;
|
||||
current_height = 0.;
|
||||
num_layers = 0;
|
||||
}
|
||||
current_height += layer->height;
|
||||
|
||||
current_height += layer.height;
|
||||
++ num_layers;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,64 @@ void ConfigManipulation::toggle_field(const std::string& opt_key, const bool tog
|
||||
cb_toggle_field(opt_key, toggle, opt_index);
|
||||
}
|
||||
|
||||
std::optional<DynamicPrintConfig> handle_automatic_extrusion_widths(const DynamicPrintConfig &config, const bool is_global_config, wxWindow *msg_dlg_parent)
|
||||
{
|
||||
const std::vector<std::string> extrusion_width_parameters = {"extrusion_width", "external_perimeter_extrusion_width", "first_layer_extrusion_width",
|
||||
"infill_extrusion_width", "perimeter_extrusion_width", "solid_infill_extrusion_width",
|
||||
"support_material_extrusion_width", "top_infill_extrusion_width"};
|
||||
|
||||
auto is_zero_width = [](const ConfigOptionFloatOrPercent &opt) -> bool {
|
||||
return opt.value == 0. && !opt.percent;
|
||||
};
|
||||
|
||||
auto is_parameters_adjustment_needed = [&is_zero_width, &config, &extrusion_width_parameters]() -> bool {
|
||||
if (!config.opt_bool("automatic_extrusion_widths")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const std::string &extrusion_width_parameter : extrusion_width_parameters) {
|
||||
if (!is_zero_width(*config.option<ConfigOptionFloatOrPercent>(extrusion_width_parameter))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (is_parameters_adjustment_needed()) {
|
||||
wxString msg_text = _(L("The automatic extrusion widths calculation requires:\n"
|
||||
"- Default extrusion width: 0\n"
|
||||
"- First layer extrusion width: 0\n"
|
||||
"- Perimeter extrusion width: 0\n"
|
||||
"- External perimeter extrusion width: 0\n"
|
||||
"- Infill extrusion width: 0\n"
|
||||
"- Solid infill extrusion width: 0\n"
|
||||
"- Top infill extrusion width: 0\n"
|
||||
"- Support material extrusion width: 0"));
|
||||
|
||||
if (is_global_config) {
|
||||
msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable automatic extrusion widths calculation?"));
|
||||
}
|
||||
|
||||
MessageDialog dialog(msg_dlg_parent, msg_text, _(L("Automatic extrusion widths calculation")),
|
||||
wxICON_WARNING | (is_global_config ? wxYES | wxNO : wxOK));
|
||||
|
||||
const int answer = dialog.ShowModal();
|
||||
DynamicPrintConfig new_conf = config;
|
||||
if (!is_global_config || answer == wxID_YES) {
|
||||
for (const std::string &extrusion_width_parameter : extrusion_width_parameters) {
|
||||
new_conf.set_key_value(extrusion_width_parameter, new ConfigOptionFloatOrPercent(0., false));
|
||||
}
|
||||
} else {
|
||||
new_conf.set_key_value("automatic_extrusion_widths", new ConfigOptionBool(false));
|
||||
}
|
||||
|
||||
return new_conf;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config)
|
||||
{
|
||||
// #ys_FIXME_to_delete
|
||||
@ -76,7 +134,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
double fill_density = config->option<ConfigOptionPercent>("fill_density")->value;
|
||||
|
||||
if (config->opt_bool("spiral_vase") &&
|
||||
! (config->opt_int("perimeters") == 1 &&
|
||||
! (config->opt_int("perimeters") == 1 &&
|
||||
config->opt_int("top_solid_layers") == 0 &&
|
||||
fill_density == 0 &&
|
||||
! config->opt_bool("support_material") &&
|
||||
@ -102,7 +160,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
new_conf.set_key_value("fill_density", new ConfigOptionPercent(0));
|
||||
new_conf.set_key_value("support_material", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0));
|
||||
new_conf.set_key_value("thin_walls", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("thin_walls", new ConfigOptionBool(false));
|
||||
fill_density = 0;
|
||||
support = false;
|
||||
}
|
||||
@ -213,6 +271,13 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config->opt_bool("automatic_extrusion_widths")) {
|
||||
std::optional<DynamicPrintConfig> new_config = handle_automatic_extrusion_widths(*config, is_global_config, m_msg_dlg_parent);
|
||||
if (new_config.has_value()) {
|
||||
apply(config, &(*new_config));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
@ -227,11 +292,17 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
toggle_field("overhang_speed_" + std::to_string(i), config->opt_bool("enable_dynamic_overhang_speeds"));
|
||||
}
|
||||
|
||||
bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
||||
const bool have_infill = config->option<ConfigOptionPercent>("fill_density")->value > 0;
|
||||
const bool has_automatic_infill_combination = config->option<ConfigOptionBool>("automatic_infill_combination")->value;
|
||||
// infill_extruder uses the same logic as in Print::extruders()
|
||||
for (auto el : { "fill_pattern", "infill_every_layers", "infill_only_where_needed",
|
||||
"solid_infill_every_layers", "solid_infill_below_area", "infill_extruder", "infill_anchor_max" })
|
||||
for (auto el : { "fill_pattern","solid_infill_every_layers", "solid_infill_below_area", "infill_extruder",
|
||||
"infill_anchor_max", "automatic_infill_combination" }) {
|
||||
toggle_field(el, have_infill);
|
||||
}
|
||||
|
||||
toggle_field("infill_every_layers", have_infill && !has_automatic_infill_combination);
|
||||
toggle_field("automatic_infill_combination_max_layer_height", have_infill && has_automatic_infill_combination);
|
||||
|
||||
// Only allow configuration of open anchors if the anchoring is enabled.
|
||||
bool has_infill_anchors = have_infill && config->option<ConfigOptionFloatOrPercent>("infill_anchor_max")->value > 0;
|
||||
toggle_field("infill_anchor", has_infill_anchors);
|
||||
|
@ -140,6 +140,8 @@ enum class NotificationType
|
||||
BedTemperaturesDiffer,
|
||||
// Notification that shrinkage compensations for the used filaments differ.
|
||||
ShrinkageCompensationsDiffer,
|
||||
// Notification about using wipe tower with different nozzle diameters.
|
||||
WipeTowerNozzleDiameterDiffer,
|
||||
};
|
||||
|
||||
class NotificationManager
|
||||
|
@ -2098,9 +2098,10 @@ void Plater::priv::process_validation_warning(const std::vector<std::string>& wa
|
||||
if (warnings.empty())
|
||||
notification_manager->close_notification_of_type(NotificationType::ValidateWarning);
|
||||
|
||||
// Always close warnings BedTemperaturesDiffer and ShrinkageCompensationsDiffer before next processing.
|
||||
// Always close warnings BedTemperaturesDiffer, ShrinkageCompensationsDiffer and WipeTowerNozzleDiameterDiffer before next processing.
|
||||
notification_manager->close_notification_of_type(NotificationType::BedTemperaturesDiffer);
|
||||
notification_manager->close_notification_of_type(NotificationType::ShrinkageCompensationsDiffer);
|
||||
notification_manager->close_notification_of_type(NotificationType::WipeTowerNozzleDiameterDiffer);
|
||||
|
||||
for (std::string text : warnings) {
|
||||
std::string hypertext = "";
|
||||
@ -2128,6 +2129,10 @@ void Plater::priv::process_validation_warning(const std::vector<std::string>& wa
|
||||
text = _u8L("Filament shrinkage will not be used because filament shrinkage "
|
||||
"for the used filaments differs significantly.");
|
||||
notification_type = NotificationType::ShrinkageCompensationsDiffer;
|
||||
} else if (text == "_WIPE_TOWER_NOZZLE_DIAMETER_DIFFER") {
|
||||
text = _u8L("Using the wipe tower for extruders with different nozzle diameters "
|
||||
"is experimental, so proceed with caution.");
|
||||
notification_type = NotificationType::WipeTowerNozzleDiameterDiffer;
|
||||
}
|
||||
|
||||
notification_manager->push_notification(
|
||||
|
@ -1503,8 +1503,9 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Reducing printing time"));
|
||||
category_path = "infill_42#";
|
||||
optgroup->append_single_option_line("automatic_infill_combination");
|
||||
optgroup->append_single_option_line("automatic_infill_combination_max_layer_height");
|
||||
optgroup->append_single_option_line("infill_every_layers", category_path + "combine-infill-every-x-layers");
|
||||
// optgroup->append_single_option_line("infill_only_where_needed", category_path + "only-infill-where-needed");
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"));
|
||||
optgroup->append_single_option_line("solid_infill_every_layers", category_path + "solid-infill-every-x-layers");
|
||||
@ -1662,6 +1663,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("solid_infill_extrusion_width");
|
||||
optgroup->append_single_option_line("top_infill_extrusion_width");
|
||||
optgroup->append_single_option_line("support_material_extrusion_width");
|
||||
optgroup->append_single_option_line("automatic_extrusion_widths");
|
||||
|
||||
optgroup = page->new_optgroup(L("Overlap"));
|
||||
optgroup->append_single_option_line("infill_overlap");
|
||||
|
Loading…
x
Reference in New Issue
Block a user