From e87b0d57b4c251d152462c854217f9f7ea2a7399 Mon Sep 17 00:00:00 2001 From: Noisyfox Date: Tue, 19 May 2026 16:24:47 +0800 Subject: [PATCH] Make `inner_wall_speed` multi-variant --- src/libslic3r/Config.cpp | 33 ++++++++++++++++++++++++++++++ src/libslic3r/Config.hpp | 14 ++++++++++--- src/libslic3r/GCode.cpp | 15 ++++++++++---- src/libslic3r/GCode.hpp | 1 + src/libslic3r/GCode/WipeTower2.cpp | 2 +- src/libslic3r/Layer.cpp | 6 +++--- src/libslic3r/Layer.hpp | 3 ++- src/libslic3r/Model.cpp | 4 ++-- src/libslic3r/Print.cpp | 4 ++-- src/libslic3r/PrintConfig.cpp | 7 ++++--- src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 13 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 89e8302904..256750a6b0 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -1886,6 +1886,39 @@ t_config_option_keys DynamicConfig::equal(const DynamicConfig &other) const return equal; } +double& DynamicConfig::opt_float(const t_config_option_key &opt_key, unsigned int idx) +{ + if (ConfigOptionFloats *opt_floats = dynamic_cast(this->option(opt_key))) { + return opt_floats->get_at(idx); + } else { + ConfigOptionFloatsNullable *opt_floats_nullable = dynamic_cast(this->option(opt_key)); + assert(opt_floats_nullable != nullptr); + return opt_floats_nullable->get_at(idx); + } +} +const double& DynamicConfig::opt_float(const t_config_option_key &opt_key, unsigned int idx) const +{ + if (const ConfigOptionFloats *opt_floats = dynamic_cast(this->option(opt_key))) { + return opt_floats->get_at(idx); + } else if (const ConfigOptionFloatsNullable *opt_floats_nullable = dynamic_cast(this->option(opt_key))) { + return opt_floats_nullable->get_at(idx); + } else { + assert(false); + return 0; + } +} + +bool DynamicConfig::opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { + if (const ConfigOptionBools *opts = dynamic_cast(this->option(opt_key))) { + return opts->get_at(idx) != 0; + } + else { + const ConfigOptionBoolsNullable *opt_s = dynamic_cast(this->option(opt_key)); + assert(opt_s != nullptr); + return opt_s->get_at(idx) != 0; + } +} + } #include diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 8ef014564c..3e5201d089 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -2900,13 +2900,17 @@ public: double& opt_float(const t_config_option_key &opt_key) { return this->option(opt_key)->value; } const double& opt_float(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key))->value; } - double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } - const double& opt_float(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + double & opt_float(const t_config_option_key &opt_key, unsigned int idx); + const double & opt_float(const t_config_option_key &opt_key, unsigned int idx) const; + double & opt_float_nullable(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } + const double & opt_float_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } int& opt_int(const t_config_option_key &opt_key) { return this->option(opt_key)->value; } int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast(this->option(opt_key))->value; } int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + int& opt_int_nullable(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx);} + const int & opt_int_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx);} // In ConfigManipulation::toggle_print_fff_options, it is called on option with type ConfigOptionEnumGeneric* and also ConfigOptionEnum*. // Thus the virtual method getInt() is used to retrieve the enum value. @@ -2914,9 +2918,13 @@ public: ENUM opt_enum(const t_config_option_key &opt_key) const { return static_cast(this->option(opt_key)->getInt()); } // BBS int opt_enum(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + int opt_enum_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + bool opt_bool(const t_config_option_key &opt_key) const { return this->option(opt_key)->value != 0; } - bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option(opt_key)->get_at(idx) != 0; } + bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const; + bool opt_bool_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx);} + // Command line processing bool read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys = nullptr); diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 9dacd594be..1b2294144d 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3590,6 +3590,13 @@ void GCode::check_placeholder_parser_failed() } } +size_t GCode::cur_extruder_index() const +{ + //TODO: check if the function is duplicated + //just return m_writer.filament()->extruder_id() + return get_extruder_id(m_writer.filament()->id()); +} + size_t GCode::get_extruder_id(unsigned int filament_id) const { if (m_print) { @@ -6436,14 +6443,14 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // set speed if (speed == -1) { if (path.role() == erPerimeter) { - speed = m_config.get_abs_value("inner_wall_speed"); + speed = m_config.inner_wall_speed.get_at(cur_extruder_index()); if (sloped) { - speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(m_config.get_abs_value("inner_wall_speed"))); + speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed)); } } else if (path.role() == erExternalPerimeter) { speed = m_config.get_abs_value("outer_wall_speed"); if (sloped) { - speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(m_config.get_abs_value("outer_wall_speed"))); + speed = std::min(speed, m_config.scarf_joint_speed.get_abs_value(speed)); } } else if(path.role() == erInternalBridgeInfill) { @@ -6578,7 +6585,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (m_config.enable_overhang_speed && !this->on_first_layer() && !object_layer_over_raft() && (is_bridge(path.role()) || is_perimeter(path.role()))) { bool is_external = is_external_perimeter(path.role()); - double ref_speed = is_external ? m_config.get_abs_value("outer_wall_speed") : m_config.get_abs_value("inner_wall_speed"); + double ref_speed = is_external ? m_config.get_abs_value("outer_wall_speed") : m_config.inner_wall_speed.get_at(cur_extruder_index()); if (ref_speed == 0) ref_speed = FILAMENT_CONFIG(filament_max_volumetric_speed) / _mm3_per_mm; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 03e76a1eff..25bf1cd904 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -383,6 +383,7 @@ private: //BBS void check_placeholder_parser_failed(); + size_t cur_extruder_index() const; size_t get_extruder_id(unsigned int filament_id) const; void set_last_pos(const Point &pos) { m_last_pos = Point3(pos, 0); m_last_pos_defined = true; } diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index c68a74f8b3..2efe3fb0f9 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -1264,7 +1264,7 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau m_gcode_flavor(config.gcode_flavor), m_travel_speed(config.travel_speed), m_infill_speed(default_region_config.sparse_infill_speed), - m_perimeter_speed(default_region_config.inner_wall_speed), + m_perimeter_speed(default_region_config.inner_wall_speed.get_at(get_extruder_index(config, (unsigned int)initial_tool))), m_current_tool(initial_tool), wipe_volumes(wiping_matrix), m_wipe_tower_max_purge_speed(float(config.wipe_tower_max_purge_speed)), m_enable_arc_fitting(config.enable_arc_fitting), diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 0a80c019ae..fdf8f7fb98 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -136,7 +136,7 @@ ExPolygons Layer::merged(float offset_scaled) const return out; } -bool Layer::is_perimeter_compatible(const PrintRegion& a, const PrintRegion& b) +bool Layer::is_perimeter_compatible(const Print& print, const PrintRegion& a, const PrintRegion& b) { const PrintRegionConfig& config = a.config(); const PrintRegionConfig& other_config = b.config(); @@ -145,7 +145,7 @@ bool Layer::is_perimeter_compatible(const PrintRegion& a, const PrintRegion& b) && config.wall_loops == other_config.wall_loops && config.wall_sequence == other_config.wall_sequence && config.is_infill_first == other_config.is_infill_first - && config.inner_wall_speed == other_config.inner_wall_speed + && config.inner_wall_speed.get_at(print.get_extruder_id(config.wall_filament)) == other_config.inner_wall_speed.get_at(print.get_extruder_id(config.wall_filament)) && config.outer_wall_speed == other_config.outer_wall_speed && config.small_perimeter_speed == other_config.small_perimeter_speed && config.gap_infill_speed.value == other_config.gap_infill_speed.value @@ -208,7 +208,7 @@ void Layer::make_perimeters() if (! (*it)->slices.empty()) { LayerRegion* other_layerm = *it; const PrintRegion &other_region = other_layerm->region(); - if (is_perimeter_compatible(this_region, other_region)) + if (is_perimeter_compatible(*m_object->print(), this_region, other_region)) { other_layerm->perimeters.clear(); other_layerm->fills.clear(); diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index cb2e6c7c1a..d871bcbea2 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -17,6 +17,7 @@ class LayerRegion; using LayerRegionPtrs = std::vector; class PrintRegion; class PrintObject; +class Print; namespace FillAdaptive { struct Octree; @@ -186,7 +187,7 @@ public: } // Whether two regions can be printed in a continues perimeter - static bool is_perimeter_compatible(const PrintRegion& a, const PrintRegion& b); + static bool is_perimeter_compatible(const Print& print, const PrintRegion& a, const PrintRegion& b); void make_perimeters(); // Phony version of make_fills() without parameters for Perl integration only. void make_fills() { this->make_fills(nullptr, nullptr); } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 0067684f15..96a3706ac8 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -2966,7 +2966,7 @@ void Model::setPrintSpeedTable(const DynamicPrintConfig& config, const PrintConf //Slic3r::DynamicPrintConfig config = wxGetApp().preset_bundle->full_config(); printSpeedMap.maxSpeed = 0; if (config.has("inner_wall_speed")) { - printSpeedMap.perimeterSpeed = config.opt_float("inner_wall_speed"); + printSpeedMap.perimeterSpeed = config.opt_float_nullable("inner_wall_speed", 0); if (printSpeedMap.perimeterSpeed > printSpeedMap.maxSpeed) printSpeedMap.maxSpeed = printSpeedMap.perimeterSpeed; } @@ -3222,7 +3222,7 @@ double Model::findMaxSpeed(const ModelObject* object) { double smallPerimeterSpeedObj = Model::printSpeedMap.smallPerimeterSpeed; for (std::string objectKey : objectKeys) { if (objectKey == "inner_wall_speed"){ - perimeterSpeedObj = object->config.opt_float(objectKey); + perimeterSpeedObj = object->config.get().opt_float_nullable(objectKey, 0); externalPerimeterSpeedObj = Model::printSpeedMap.externalPerimeterSpeed / Model::printSpeedMap.perimeterSpeed * perimeterSpeedObj; } if (objectKey == "sparse_infill_speed") diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f2e59c3b67..cab707b69d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1343,8 +1343,8 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons* const auto all_regions = m_objects.front()->all_regions(); if (all_regions.size() > 1) { // Orca: make sure regions are not compatible - if (std::any_of(all_regions.begin() + 1, all_regions.end(), [ra = all_regions.front()](const auto rb) { - return !Layer::is_perimeter_compatible(ra, rb); + if (std::any_of(all_regions.begin() + 1, all_regions.end(), [this, ra = all_regions.front()](const auto rb) { + return !Layer::is_perimeter_compatible(*this, ra, rb); })) { return {L("The spiral vase mode does not work when an object contains more than one materials."), nullptr, "spiral_mode"}; } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index e041d8a147..c6bcc75510 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4905,7 +4905,7 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloatOrPercent(0., false)); - def = this->add("inner_wall_speed", coFloat); + def = this->add("inner_wall_speed", coFloats); def->label = L("Inner wall"); def->category = L("Speed"); def->tooltip = L("Speed of inner wall."); @@ -4913,7 +4913,8 @@ void PrintConfigDef::init_fff_params() def->aliases = { "perimeter_feed_rate" }; def->min = 1; def->mode = comAdvanced; - def->set_default_value(new ConfigOptionFloat(60)); + def->nullable = true; + def->set_default_value(new ConfigOptionFloatsNullable{60}); def = this->add("wall_loops", coInt); def->label = L("Wall loops"); @@ -8157,7 +8158,7 @@ std::set print_options_with_variant = { //"initial_layer_speed", //"initial_layer_infill_speed", //"outer_wall_speed", - //"inner_wall_speed", + "inner_wall_speed", //"small_perimeter_speed", //coFloatsOrPercents //"small_perimeter_threshold", //"sparse_infill_speed", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8ccc6d304a..880fd44389 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1164,7 +1164,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, detect_overhang_wall)) ((ConfigOptionInt, wall_filament)) ((ConfigOptionFloatOrPercent, inner_wall_line_width)) - ((ConfigOptionFloat, inner_wall_speed)) + ((ConfigOptionFloatsNullable, inner_wall_speed)) // Total number of perimeters. ((ConfigOptionInt, wall_loops)) ((ConfigOptionBool, alternate_extra_wall)) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index b407fdbc66..aaeb1ac23e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -12943,7 +12943,7 @@ void Plater::_calib_pa_tower(const Calib_Params& params) { full_config, full_config.get_abs_value("line_width", nozzle_diameter), full_config.get_abs_value("layer_height"), 0, 0); obj_cfg.set_key_value("outer_wall_speed", new ConfigOptionFloat(wall_speed)); - obj_cfg.set_key_value("inner_wall_speed", new ConfigOptionFloat(wall_speed)); + obj_cfg.set_key_value("inner_wall_speed", new ConfigOptionFloatsNullable({wall_speed})); obj_cfg.set_key_value("seam_position", new ConfigOptionEnum(spRear)); obj_cfg.set_key_value("wall_loops", new ConfigOptionInt(2)); obj_cfg.set_key_value("top_shell_layers", new ConfigOptionInt(0)); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 69cb82e09f..aeddd53f8a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2762,7 +2762,7 @@ void TabPrint::build() optgroup->append_single_option_line("slow_down_layers", "speed_settings_initial_layer_speed#number-of-slow-layers"); optgroup = page->new_optgroup(L("Other layers speed"), L"param_speed", 15); optgroup->append_single_option_line("outer_wall_speed", "speed_settings_other_layers_speed#outer-wall"); - optgroup->append_single_option_line("inner_wall_speed", "speed_settings_other_layers_speed#inner-wall"); + optgroup->append_single_option_line("inner_wall_speed", "speed_settings_other_layers_speed#inner-wall", 0); optgroup->append_single_option_line("small_perimeter_speed", "speed_settings_other_layers_speed#small-perimeters"); optgroup->append_single_option_line("small_perimeter_threshold", "speed_settings_other_layers_speed#small-perimeters-threshold"); optgroup->append_single_option_line("sparse_infill_speed", "speed_settings_other_layers_speed#sparse-infill");