diff --git a/resources/ui_layout/default/filament.ui b/resources/ui_layout/default/filament.ui index 845f5e3773b..5b9ad60732d 100644 --- a/resources/ui_layout/default/filament.ui +++ b/resources/ui_layout/default/filament.ui @@ -1,97 +1,96 @@ - +#note: all settings are id$0 as the slicer consider there is only one value in the arrays in the gui. page:Filament:spool group:filament_spool_weight_event:Filament - setting:filament_colour - setting:filament_diameter - setting:extrusion_multiplier - setting:filament_density - setting:filament_cost - setting:filament_spool_weight + setting:id$0:filament_colour + setting:id$0:filament_diameter + setting:id$0:extrusion_multiplier + setting:id$0:filament_density + setting:id$0:filament_cost + setting:id$0:filament_spool_weight group:Temperature °C line:Extruder - setting:first_layer_temperature - setting:temperature + setting:id$0:first_layer_temperature + setting:id$0:temperature end_line line:Bed - setting:first_layer_bed_temperature - setting:label:Other layers:bed_temperature + setting:id$0:first_layer_bed_temperature + setting:id$0:label:Other layers:bed_temperature end_line - setting:chamber_temperature + setting:id$0:chamber_temperature group:Filament properties - setting:width$7:filament_type - setting:filament_soluble - setting:filament_shrink - setting:filament_max_overlap + setting:id$0:width$7:filament_type + setting:id$0:filament_soluble + setting:id$0:filament_shrink + setting:id$0:filament_max_overlap group:Print speed override - setting:filament_max_speed - setting:filament_max_volumetric_speed + setting:id$0:filament_max_speed + setting:id$0:filament_max_volumetric_speed volumetric_speed_description page:Cooling:time group:Fan speed - default - setting:label$Run the fan at default speed when possible:fan_always_on - line:Disable fan for the first - setting:width$5:label$_:sidetext_width$7:disable_fan_first_layers - setting:width$5:label_width$12:full_fan_speed_layer - end_line - setting:min_fan_speed + setting:id$0:default_fan_speed line:Perimeter fan speed - setting:label_width$12:label$Internal:perimeter_fan_speed - setting:label_width$12:label$External:external_perimeter_fan_speed + setting:id$0:label_width$12:label$Internal:perimeter_fan_speed + setting:id$0:label_width$12:label$External:external_perimeter_fan_speed line:Internal Infill fan speed - setting:label_width$12:label$Sparse:infill_fan_speed + setting:id$0:label_width$12:label$Sparse:infill_fan_speed line:Solid Infill fan speed - setting:label_width$12:label$Solid:solid_infill_fan_speed - setting:label_width$12:label$Top solid:top_fan_speed + setting:id$0:label_width$12:label$Solid:solid_infill_fan_speed + setting:id$0:label_width$12:label$Top solid:top_fan_speed line:Support Material fan speed - setting:label_width$12:label$Default:support_material_fan_speed - setting:label_width$12:label$Interface:support_material_interface_fan_speed + setting:id$0:label_width$12:label$Default:support_material_fan_speed + setting:id$0:label_width$12:label$Interface:support_material_interface_fan_speed line:Bridges fan speed - setting:label_width$12:label$Bridges:bridge_fan_speed - setting:label_width$12:label$Internal bridges:bridge_internal_fan_speed + setting:id$0:label_width$12:label$Bridges:bridge_fan_speed + setting:id$0:label_width$12:label$Internal bridges:bridge_internal_fan_speed line:Overhangs Perimeter fan speed - setting:label_width$12:label$Overhangs:overhangs_fan_speed + setting:id$0:label_width$12:label$Overhangs:overhangs_fan_speed line:Gap fill fan speed - setting:label_width$12:label$Gap fill:gap_fill_fan_speed + setting:id$0:label_width$12:label$Gap fill:gap_fill_fan_speed + line:Disable fan for the first + setting:id$0:width$5:label$_:sidetext_width$7:disable_fan_first_layers + setting:id$0:width$5:label_width$12:full_fan_speed_layer + end_line group:Short layer time - began to increase base fan speed - setting:fan_below_layer_time - setting:label$Max fan speed:max_fan_speed + setting:id$0:fan_below_layer_time + setting:id$0:label$Max fan speed:max_fan_speed group:Very short layer time - began to decrease extrusion rate - setting:label$Layer time goal:slowdown_below_layer_time - setting:width$4:max_speed_reduction - setting:width$4:min_print_speed + setting:id$0:label$Layer time goal:slowdown_below_layer_time + setting:id$0:width$4:max_speed_reduction + setting:id$0:width$4:min_print_speed group:Behavior cooling_description page:Multimaterial:funnel group:Multimaterial toolchange temperature - setting:filament_enable_toolchange_temp - setting:filament_toolchange_temp - setting:filament_use_fast_skinnydip - setting:filament_enable_toolchange_part_fan - setting:filament_toolchange_part_fan_speed + setting:id$0:filament_enable_toolchange_temp + setting:id$0:filament_toolchange_temp + setting:id$0:filament_use_fast_skinnydip + setting:id$0:filament_enable_toolchange_part_fan + setting:id$0:filament_toolchange_part_fan_speed group:Multimaterial toolchange string reduction - setting:filament_use_skinnydip - setting:filament_skinnydip_distance - setting:filament_melt_zone_pause - setting:filament_cooling_zone_pause - setting:filament_dip_insertion_speed - setting:filament_dip_extraction_speed + setting:id$0:filament_use_skinnydip + setting:id$0:filament_skinnydip_distance + setting:id$0:filament_melt_zone_pause + setting:id$0:filament_cooling_zone_pause + setting:id$0:filament_dip_insertion_speed + setting:id$0:filament_dip_extraction_speed group:Wipe tower parameters - setting:filament_minimal_purge_on_wipe_tower - setting:filament_max_wipe_tower_speed + setting:id$0:filament_minimal_purge_on_wipe_tower + setting:id$0:filament_max_wipe_tower_speed group:Toolchange parameters with single extruder MM printers - setting:filament_loading_speed_start - setting:filament_loading_speed - setting:filament_unloading_speed_start - setting:filament_unloading_speed - setting:filament_load_time - setting:filament_unload_time - setting:filament_toolchange_delay - setting:filament_cooling_moves - setting:filament_cooling_initial_speed - setting:filament_cooling_final_speed - setting:filament_wipe_advanced_pigment + setting:id$0:filament_loading_speed_start + setting:id$0:filament_loading_speed + setting:id$0:filament_unloading_speed_start + setting:id$0:filament_unloading_speed + setting:id$0:filament_load_time + setting:id$0:filament_unload_time + setting:id$0:filament_toolchange_delay + setting:id$0:filament_cooling_moves + setting:id$0:filament_cooling_initial_speed + setting:id$0:filament_cooling_final_speed + setting:id$0:filament_wipe_advanced_pigment filament_ramming_parameters @@ -99,20 +98,20 @@ filament_overrides_page page:Custom G-code:cog group:no_title:validate_gcode:Start G-code - setting:full_width:height$35:start_filament_gcode + setting:id$0:full_width:height$35:start_filament_gcode group:no_title:validate_gcode:End G-code - setting:full_width:height$35:end_filament_gcode + setting:id$0:full_width:height$35:end_filament_gcode page:Notes:note.png group:label_width$0:Notes - setting:full_width:height$25:filament_notes + setting:id$0:full_width:height$25:filament_notes group:label_width$0:Custom variables - setting:full_width:height$15:filament_custom_variables + setting:id$0:full_width:height$15:filament_custom_variables page:Dependencies:wrench.png group:Profile dependencies - setting:compatible_printers - setting:full_width:color:compatible_printers_condition - setting:compatible_prints - setting:full_width:color:compatible_prints_condition + setting:id$0:compatible_printers + setting:id$0:full_width:color:compatible_printers_condition + setting:id$0:compatible_prints + setting:id$0:full_width:color:compatible_prints_condition parent_preset_description diff --git a/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui index 39807c5bef4..a1b54e6b2b7 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -193,14 +193,19 @@ group:Reducing printing time setting:tags$Advanced$Expert$SuSi:sidetext_width$0:label$_:infill_dense setting:width$20:infill_dense_algo end_line -group:sidetext_width$5:Advanced - setting:solid_infill_every_layers - setting:solid_infill_below_area +group:sidetext_width$5:Infill angle line:Angle setting:label_width$8:width$5:fill_angle setting:label_width$6:width$5:bridge_angle - setting:label_width$6:width$5:label$increment:fill_angle_increment end_line + line:Modifiers + setting:label_width$6:width$5:label$increment:fill_angle_increment + setting:label_width$6:width$5:label$increment:fill_angle_cross + vector_line:fill_angle_template +# setting:fill_angle_template +group:sidetext_width$5:Advanced + setting:solid_infill_every_layers + setting:solid_infill_below_area line:Anchor solid infill by X mm setting:label_width$8:width$5:external_infill_margin setting:label_width$6:width$5:bridged_infill_margin diff --git a/resources/ui_layout/default/printer_fff.ui b/resources/ui_layout/default/printer_fff.ui index 5c016f12231..f7d8c48deae 100644 --- a/resources/ui_layout/default/printer_fff.ui +++ b/resources/ui_layout/default/printer_fff.ui @@ -38,6 +38,7 @@ group:silent_mode_event:Firmware end_line setting:gcode_filename_illegal_char group:Cooling fan + setting:fan_printer_min_speed line:Speedup time setting:label$:fan_speedup_time setting:label$Only for overhangs:fan_speedup_overhangs diff --git a/resources/ui_layout/example/filament.ui b/resources/ui_layout/example/filament.ui index 845f5e3773b..7aefcd3fbd8 100644 --- a/resources/ui_layout/example/filament.ui +++ b/resources/ui_layout/example/filament.ui @@ -1,97 +1,97 @@ page:Filament:spool group:filament_spool_weight_event:Filament - setting:filament_colour - setting:filament_diameter - setting:extrusion_multiplier - setting:filament_density - setting:filament_cost - setting:filament_spool_weight + setting:id$0:filament_colour + setting:id$0:filament_diameter + setting:id$0:extrusion_multiplier + setting:id$0:filament_density + setting:id$0:filament_cost + setting:id$0:filament_spool_weight group:Temperature °C line:Extruder - setting:first_layer_temperature - setting:temperature + setting:id$0:first_layer_temperature + setting:id$0:temperature end_line line:Bed - setting:first_layer_bed_temperature - setting:label:Other layers:bed_temperature + setting:id$0:first_layer_bed_temperature + setting:id$0:label:Other layers:bed_temperature end_line - setting:chamber_temperature + setting:id$0:chamber_temperature group:Filament properties - setting:width$7:filament_type - setting:filament_soluble - setting:filament_shrink - setting:filament_max_overlap + setting:id$0:width$7:filament_type + setting:id$0:filament_soluble + setting:id$0:filament_shrink + setting:id$0:filament_max_overlap group:Print speed override - setting:filament_max_speed - setting:filament_max_volumetric_speed + setting:id$0:filament_max_speed + setting:id$0:filament_max_volumetric_speed volumetric_speed_description page:Cooling:time group:Fan speed - default - setting:label$Run the fan at default speed when possible:fan_always_on + setting:id$0:label$Run the fan at default speed when possible:fan_always_on line:Disable fan for the first - setting:width$5:label$_:sidetext_width$7:disable_fan_first_layers - setting:width$5:label_width$12:full_fan_speed_layer + setting:id$0:width$5:label$_:sidetext_width$7:disable_fan_first_layers + setting:id$0:width$5:label_width$12:full_fan_speed_layer end_line - setting:min_fan_speed + setting:id$0:min_fan_speed line:Perimeter fan speed - setting:label_width$12:label$Internal:perimeter_fan_speed - setting:label_width$12:label$External:external_perimeter_fan_speed + setting:id$0:label_width$12:label$Internal:perimeter_fan_speed + setting:id$0:label_width$12:label$External:external_perimeter_fan_speed line:Internal Infill fan speed - setting:label_width$12:label$Sparse:infill_fan_speed + setting:id$0:label_width$12:label$Sparse:infill_fan_speed line:Solid Infill fan speed - setting:label_width$12:label$Solid:solid_infill_fan_speed - setting:label_width$12:label$Top solid:top_fan_speed + setting:id$0:label_width$12:label$Solid:solid_infill_fan_speed + setting:id$0:label_width$12:label$Top solid:top_fan_speed line:Support Material fan speed - setting:label_width$12:label$Default:support_material_fan_speed - setting:label_width$12:label$Interface:support_material_interface_fan_speed + setting:id$0:label_width$12:label$Default:support_material_fan_speed + setting:id$0:label_width$12:label$Interface:support_material_interface_fan_speed line:Bridges fan speed - setting:label_width$12:label$Bridges:bridge_fan_speed - setting:label_width$12:label$Internal bridges:bridge_internal_fan_speed + setting:id$0:label_width$12:label$Bridges:bridge_fan_speed + setting:id$0:label_width$12:label$Internal bridges:bridge_internal_fan_speed line:Overhangs Perimeter fan speed - setting:label_width$12:label$Overhangs:overhangs_fan_speed + setting:id$0:label_width$12:label$Overhangs:overhangs_fan_speed line:Gap fill fan speed - setting:label_width$12:label$Gap fill:gap_fill_fan_speed + setting:id$0:label_width$12:label$Gap fill:gap_fill_fan_speed group:Short layer time - began to increase base fan speed - setting:fan_below_layer_time - setting:label$Max fan speed:max_fan_speed + setting:id$0:fan_below_layer_time + setting:id$0:label$Max fan speed:max_fan_speed group:Very short layer time - began to decrease extrusion rate - setting:label$Layer time goal:slowdown_below_layer_time - setting:width$4:max_speed_reduction - setting:width$4:min_print_speed + setting:id$0:label$Layer time goal:slowdown_below_layer_time + setting:id$0:width$4:max_speed_reduction + setting:id$0:width$4:min_print_speed group:Behavior cooling_description page:Multimaterial:funnel group:Multimaterial toolchange temperature - setting:filament_enable_toolchange_temp - setting:filament_toolchange_temp - setting:filament_use_fast_skinnydip - setting:filament_enable_toolchange_part_fan - setting:filament_toolchange_part_fan_speed + setting:id$0:filament_enable_toolchange_temp + setting:id$0:filament_toolchange_temp + setting:id$0:filament_use_fast_skinnydip + setting:id$0:filament_enable_toolchange_part_fan + setting:id$0:filament_toolchange_part_fan_speed group:Multimaterial toolchange string reduction - setting:filament_use_skinnydip - setting:filament_skinnydip_distance - setting:filament_melt_zone_pause - setting:filament_cooling_zone_pause - setting:filament_dip_insertion_speed - setting:filament_dip_extraction_speed + setting:id$0:filament_use_skinnydip + setting:id$0:filament_skinnydip_distance + setting:id$0:filament_melt_zone_pause + setting:id$0:filament_cooling_zone_pause + setting:id$0:filament_dip_insertion_speed + setting:id$0:filament_dip_extraction_speed group:Wipe tower parameters - setting:filament_minimal_purge_on_wipe_tower - setting:filament_max_wipe_tower_speed + setting:id$0:filament_minimal_purge_on_wipe_tower + setting:id$0:filament_max_wipe_tower_speed group:Toolchange parameters with single extruder MM printers - setting:filament_loading_speed_start - setting:filament_loading_speed - setting:filament_unloading_speed_start - setting:filament_unloading_speed - setting:filament_load_time - setting:filament_unload_time - setting:filament_toolchange_delay - setting:filament_cooling_moves - setting:filament_cooling_initial_speed - setting:filament_cooling_final_speed - setting:filament_wipe_advanced_pigment + setting:id$0:filament_loading_speed_start + setting:id$0:filament_loading_speed + setting:id$0:filament_unloading_speed_start + setting:id$0:filament_unloading_speed + setting:id$0:filament_load_time + setting:id$0:filament_unload_time + setting:id$0:filament_toolchange_delay + setting:id$0:filament_cooling_moves + setting:id$0:filament_cooling_initial_speed + setting:id$0:filament_cooling_final_speed + setting:id$0:filament_wipe_advanced_pigment filament_ramming_parameters @@ -99,20 +99,20 @@ filament_overrides_page page:Custom G-code:cog group:no_title:validate_gcode:Start G-code - setting:full_width:height$35:start_filament_gcode + setting:id$0:full_width:height$35:start_filament_gcode group:no_title:validate_gcode:End G-code - setting:full_width:height$35:end_filament_gcode + setting:id$0:full_width:height$35:end_filament_gcode page:Notes:note.png group:label_width$0:Notes - setting:full_width:height$25:filament_notes + setting:id$0:full_width:height$25:filament_notes group:label_width$0:Custom variables - setting:full_width:height$15:filament_custom_variables + setting:id$0:full_width:height$15:filament_custom_variables page:Dependencies:wrench.png group:Profile dependencies - setting:compatible_printers - setting:full_width:color:compatible_printers_condition - setting:compatible_prints - setting:full_width:color:compatible_prints_condition + setting:id$0:compatible_printers + setting:id$0:full_width:color:compatible_printers_condition + setting:id$0:compatible_prints + setting:id$0:full_width:color:compatible_prints_condition parent_preset_description diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 5b875bbc751..7eedfd923dd 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -237,8 +237,8 @@ int CLI::run(int argc, char **argv) // The default bed shape should reflect the default display parameters // and not the fff defaults. - double w = sla_print_config.display_width.getFloat(); - double h = sla_print_config.display_height.getFloat(); + double w = sla_print_config.display_width.get_float(); + double h = sla_print_config.display_height.get_float(); sla_print_config.bed_shape.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) }; sla_print_config.apply(m_print_config, true); diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index c603e316a1e..19ed3c2001c 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -320,7 +320,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const if (this->default_value) return (this->default_value->type() == coEnum) ? // Special case: For a DynamicConfig, convert a templated enum to a generic enum. - new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) : + new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->get_int()) : this->default_value->clone(); return this->create_empty_option(); } @@ -781,12 +781,12 @@ double ConfigBase::get_computed_value(const t_config_option_key &opt_key, int ex if (extruder_id < 0) { const ConfigOption* opt_extruder_id = nullptr; if ((opt_extruder_id = this->option("extruder")) == nullptr) - if ((opt_extruder_id = this->option("current_extruder")) == nullptr - || opt_extruder_id->getInt() < 0 || opt_extruder_id->getInt() >= vector_opt->size()) { + if ((opt_extruder_id = this->option("current_extruder")) == nullptr || + opt_extruder_id->get_int() < 0 || opt_extruder_id->get_int() >= vector_opt->size()) { std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " need to has the extuder id to get the right value, but it's not available"; throw ConfigurationError(ss.str()); } - extruder_id = opt_extruder_id->getInt(); + extruder_id = opt_extruder_id->get_int(); idx = extruder_id; } } else { @@ -797,7 +797,7 @@ double ConfigBase::get_computed_value(const t_config_option_key &opt_key, int ex } if (idx >= 0) { if (raw_opt->type() == coFloats || raw_opt->type() == coInts || raw_opt->type() == coBools) - return vector_opt->getFloat(idx); + return vector_opt->get_float(idx); if (raw_opt->type() == coFloatsOrPercents) { const ConfigOptionFloatsOrPercents* opt_fl_per = static_cast(raw_opt); if (!opt_fl_per->values[idx].percent) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index e046e49cfe2..e4e79328778 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -81,6 +81,7 @@ extern bool unescape_strings_cstyle(const std::string &str, std::vector< extern std::string escape_ampersand(const std::string& str); +constexpr char NIL_STR_VALUE[] = "nil"; enum class OptionCategory : int { @@ -395,11 +396,12 @@ class ConfigOption { virtual ConfigOption* clone() const = 0; // Set a value from a ConfigOption. The two options should be compatible. virtual void set(const ConfigOption *option) = 0; - virtual int32_t getInt() const { throw BadOptionTypeException("Calling ConfigOption::getInt on a non-int ConfigOption"); } - virtual double getFloat() const { throw BadOptionTypeException("Calling ConfigOption::getFloat on a non-float ConfigOption"); } - virtual bool getBool() const { throw BadOptionTypeException("Calling ConfigOption::getBool on a non-boolean ConfigOption"); } - virtual void setInt(int32_t /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); } - virtual boost::any getAny() const { throw BadOptionTypeException("Calling ConfigOption::getBool on a non-boolean ConfigOption"); } + virtual int32_t get_int(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_int on a non-int ConfigOption"); } + virtual double get_float(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_float on a non-float ConfigOption"); } + virtual bool get_bool(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_bool on a non-boolean ConfigOption"); } + virtual void set_enum_int(int32_t /* val */) { throw BadOptionTypeException("Calling ConfigOption::set_enum_int on a non-enum ConfigOption"); } + virtual boost::any get_any(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_any on a raw ConfigOption"); } + virtual void set_any(boost::any, int idx = -1) { throw BadOptionTypeException("Calling ConfigOption::set_any on a raw ConfigOption"); } virtual bool operator==(const ConfigOption &rhs) const = 0; bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } virtual size_t hash() const throw() = 0; @@ -407,8 +409,8 @@ class ConfigOption { bool is_vector() const { return ! this->is_scalar(); } // If this option is nullable, then it may have its value or values set to nil. virtual bool nullable() const { return false; } - // A scalar is nil, or all values of a vector are nil. - virtual bool is_nil() const { return false; } + // A scalar is nil, or all values of a vector are nil if idx < 0. + virtual bool is_nil(int idx = -1) const { return false; } bool is_phony() const { return (flags & FCO_PHONY) != 0; } void set_phony(bool phony) { if (phony) this->flags |= FCO_PHONY; else this->flags &= uint8_t(0xFF ^ FCO_PHONY); } // Is this option overridden by another option? @@ -440,7 +442,8 @@ class ConfigOptionSingle : public ConfigOption { explicit ConfigOptionSingle(T value) : value(value) {} explicit ConfigOptionSingle(T value, bool phony) : ConfigOption(phony), value(value) {} operator T() const { return this->value; } - virtual boost::any getAny() const { return boost::any(value); } + boost::any get_any(int idx = 0) const override { return boost::any(value); } + void set_any(boost::any anyval, int idx = -1) override { value = boost::any_cast(anyval); } void set(const ConfigOption *rhs) override { @@ -487,25 +490,25 @@ class ConfigOptionVectorBase : public ConfigOption { virtual void resize(size_t n, const ConfigOption *opt_default = nullptr) = 0; // Clear the values vector. virtual void clear() = 0; + // get the stored default value for filling empty vector. + // If you use it, double check if you shouldn't instead use the ConfigOptionDef.defaultvalue, which is the default value of a setting. + // currently, it's used to try to have a meaningful value for a Field if the default value is Nil (and to avoid cloning the option, clear it, asking for an item) + virtual boost::any get_default_value() const = 0; // Get size of this vector. virtual size_t size() const = 0; // Is this vector empty? virtual bool empty() const = 0; - // Is the value nil? That should only be possible if this->nullable(). - virtual bool is_nil(size_t idx) const = 0; // Get if the size of this vector is/should be the same as nozzle_diameter bool is_extruder_size() const { return (flags & FCO_EXTRUDER_ARRAY) != 0; } ConfigOptionVectorBase* set_is_extruder_size(bool is_extruder_size) { if (is_extruder_size) this->flags |= FCO_EXTRUDER_ARRAY; else this->flags &= uint8_t(0xFF ^ FCO_EXTRUDER_ARRAY); return this; } - virtual double getFloat(int idx) const { throw BadOptionTypeException("Calling ConfigOption::getFloat(idx) on a non-numeric arrray ConfigOptionVectorBase"); } // We just overloaded and hid two base class virtual methods. // Let's show it was intentional (warnings). using ConfigOption::set; - using ConfigOption::is_nil; protected: @@ -514,18 +517,28 @@ class ConfigOptionVectorBase : public ConfigOption { }; // Value of a vector valued option (bools, ints, floats, strings, points), template -template -class ConfigOptionVector : public ConfigOptionVectorBase +template class ConfigOptionVector : public ConfigOptionVectorBase { +private: + void set_default_from_values() { + assert(!values.empty()); + if (!values.empty()) + default_value = values.front(); + } + +protected: + // this default is used to fill this vector when resized. It's not the default of a setting, for it please use the + // ConfigOptionDef. + T default_value; public: + std::vector values; + ConfigOptionVector() {} explicit ConfigOptionVector(const T& default_val) : default_value(default_val) {} - explicit ConfigOptionVector(size_t n, const T& value) : values(n, value) {} - explicit ConfigOptionVector(std::initializer_list il) : values(std::move(il)) {} - explicit ConfigOptionVector(const std::vector &values) : values(values) {} - explicit ConfigOptionVector(std::vector &&values) : values(std::move(values)) {} - std::vector values; - T default_value; + explicit ConfigOptionVector(size_t n, const T &value) : values(n, value), default_value(value) {} + explicit ConfigOptionVector(std::initializer_list il) : values(std::move(il)) { set_default_from_values(); } + explicit ConfigOptionVector(const std::vector &values) : values(values) { set_default_from_values(); } + explicit ConfigOptionVector(std::vector &&values) : values(std::move(values)) { set_default_from_values(); } void set(const ConfigOption *rhs) override { @@ -561,12 +574,10 @@ class ConfigOptionVector : public ConfigOptionVectorBase // This function is useful to split values from multiple extrder / filament settings into separate configurations. void set_at(const ConfigOption *rhs, size_t i, size_t j) override { - // It is expected that the vector value has at least one value, which is the default, if not overwritten. - assert(! this->values.empty()); + // Fill with default value up to the needed position if (this->values.size() <= i) { // Resize this vector, fill in the new vector fields with the copy of the first field. - T v = this->values.front(); - this->values.resize(i + 1, v); + this->values.resize(i + 1, this->default_value); } if (rhs->type() == this->type()) { // Assign the first value of the rhs vector. @@ -581,12 +592,10 @@ class ConfigOptionVector : public ConfigOptionVectorBase } void set_at(T val, size_t i) { - // It is expected that the vector value has at least one value, which is the default, if not overwritten. - assert(!this->values.empty()); + // Fill with default value up to the needed position if (this->values.size() <= i) { // Resize this vector, fill in the new vector fields with the copy of the first field. - T v = this->values.front(); - this->values.resize(i + 1, v); + this->values.resize(i + 1, this->default_value); } this->values[i] = val; } @@ -598,15 +607,22 @@ class ConfigOptionVector : public ConfigOptionVectorBase } T& get_at(size_t i) { return const_cast(std::as_const(*this).get_at(i)); } - virtual boost::any getAny() const { return boost::any(values); } + boost::any get_any(int idx) const override { return idx < 0 ? boost::any(values) : boost::any(get_at(idx)); } + void set_any(boost::any anyval, int idx = -1) override + { + if (idx < 0) + values = boost::any_cast>(anyval); + else + set_at(boost::any_cast(anyval), idx); + } - // Resize this vector by duplicating the /*last*/first value. + // Resize this vector by duplicating the /*last*/first or default value. // If the current vector is empty, the default value is used instead. void resize(size_t n, const ConfigOption *opt_default = nullptr) override { assert(opt_default == nullptr || opt_default->is_vector()); // assert(opt_default == nullptr || dynamic_cast>(opt_default)); - assert(! this->values.empty() || opt_default != nullptr); + // assert(! this->values.empty() || opt_default != nullptr); if (n == 0) this->values.clear(); else if (n < this->values.size()) @@ -617,12 +633,12 @@ class ConfigOptionVector : public ConfigOptionVectorBase this->values.resize(n, this->default_value); if (opt_default->type() != this->type()) throw ConfigurationError("ConfigOptionVector::resize(): Extending with an incompatible type."); - if(static_cast*>(opt_default)->values.empty()) - this->values.resize(n, this->default_value); + if(auto other = static_cast*>(opt_default); other->values.empty()) + this->values.resize(n, other->default_value); else - this->values.resize(n, static_cast*>(opt_default)->values.front()); + this->values.resize(n, other->values.front()); } else { - // Resize by duplicating the last value. + // Resize by duplicating the /*last*/first value. this->values.resize(n, this->values./*back*/front()); } } @@ -632,6 +648,10 @@ class ConfigOptionVector : public ConfigOptionVectorBase void clear() override { this->values.clear(); } size_t size() const override { return this->values.size(); } bool empty() const override { return this->values.empty(); } + // get the stored default value for filling empty vector. + // If you use it, double check if you shouldn't instead use the ConfigOptionDef.defaultvalue, which is the default value of a setting. + // currently, it's used to try to have a meaningful value for a Field if the default value is Nil + boost::any get_default_value() const override { return boost::any(default_value); } bool operator==(const ConfigOption &rhs) const override { @@ -698,10 +718,7 @@ class ConfigOptionVector : public ConfigOptionVectorBase } for (; i < rhs_vec->size(); ++ i) if (! rhs_vec->is_nil(i)) { - if (this->values.empty()) - this->values.resize(i + 1); - else - this->values.resize(i + 1, this->values.front()); + this->values.resize(i + 1, this->default_value); this->values[i] = rhs_vec->values[i]; modified = true; } @@ -722,7 +739,7 @@ class ConfigOptionFloat : public ConfigOptionSingle static ConfigOptionType static_type() { return coFloat; } ConfigOptionType type() const override { return static_type(); } - double getFloat() const override { return this->value; } + double get_float(int idx = 0) const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; } bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; } @@ -777,19 +794,38 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector } // Could a special "nil" value be stored inside the vector, indicating undefined value? bool nullable() const override { return NULLABLE; } - // Special "nil" value to be stored into the vector if this->supports_nil(). - static double nil_value() { return std::numeric_limits::quiet_NaN(); } - // A scalar is nil, or all values of a vector are nil. - bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v)) return false; return true; } - bool is_nil(size_t idx) const override { return idx < values.size() ? std::isnan(this->values[idx]) : values.empty() ? std::isnan(this->default_value) : std::isnan(this->values.front()); } - virtual double getFloat(int idx) const override { return values[idx]; } + double get_float(int idx = 0) const override { return get_at(idx); } + + bool is_nil(int idx = 0) const override + { + if (idx < 0) { + for (double v : this->values) + if (!std::isnan(v) && v != NIL_VALUE()) + return false; + return true; + } else { + return idx < values.size() ? (std::isnan(this->values[idx]) || NIL_VALUE() == this->values[idx]) : + values.empty() ? (std::isnan(this->default_value) || NIL_VALUE() == this->default_value) : + (std::isnan(this->values.front()) || NIL_VALUE() == this->values.front()); + } + } + + static inline bool is_nil(const boost::any &to_check) + { + return std::isnan(boost::any_cast(to_check)) || boost::any_cast(to_check) == NIL_VALUE(); + } + // don't use it to compare, use is_nil() to check. + static inline boost::any create_nil() + { + return boost::any(NIL_VALUE()); + } std::string serialize() const override { std::ostringstream ss; for (const double &v : this->values) { if (&v != &this->values.front()) - ss << ","; + ss << ","; serialize_single_value(ss, v); } return ss.str(); @@ -815,9 +851,9 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector std::string item_str; while (std::getline(is, item_str, ',')) { boost::trim(item_str); - if (item_str == "nil") { + if (item_str == NIL_STR_VALUE) { if (NULLABLE) - this->values.push_back(nil_value()); + this->values.push_back(NIL_VALUE()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { @@ -837,12 +873,16 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector } protected: + // Special "nil" value to be stored into the vector if this->supports_nil(). + // try to not use nan. It's a weird value, and other types don't use it. + static double NIL_VALUE() { return std::numeric_limits::quiet_NaN(); } // std::numeric_limits::max(); } + void serialize_single_value(std::ostringstream &ss, const double v) const { if (std::isfinite(v)) ss << v; - else if (std::isnan(v)) { + else if (std::isnan(v) || v == NIL_VALUE()) { if (NULLABLE) - ss << "nil"; + ss << NIL_STR_VALUE; else throw ConfigurationError("Serializing NaN"); } else @@ -853,7 +893,8 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector if (v1.size() != v2.size()) return false; for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end(); ++ it1, ++ it2) - if (! ((std::isnan(*it1) && std::isnan(*it2)) || *it1 == *it2)) + if (!(((std::isnan(*it1) || *it1 == NIL_VALUE()) && (std::isnan(*it2) || *it2 == NIL_VALUE())) || + *it1 == *it2)) return false; return true; } else @@ -863,8 +904,8 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector static bool vectors_lower(const std::vector &v1, const std::vector &v2) { if (NULLABLE) { for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end() && it2 != v2.end(); ++ it1, ++ it2) { - auto null1 = int(std::isnan(*it1)); - auto null2 = int(std::isnan(*it2)); + auto null1 = int(std::isnan(*it1) || *it1 == NIL_VALUE()); + auto null2 = int(std::isnan(*it2) || *it2 == NIL_VALUE()); return (null1 < null2) || (null1 == null2 && *it1 < *it2); } return v1.size() < v2.size(); @@ -890,8 +931,8 @@ class ConfigOptionInt : public ConfigOptionSingle static ConfigOptionType static_type() { return coInt; } ConfigOptionType type() const override { return static_type(); } - int32_t getInt() const override { return this->value; } - void setInt(int32_t val) override { this->value = val; } + int32_t get_int(int idx = 0) const override { return this->value; } + double get_float(int idx = 0) const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionInt(*this); } bool operator==(const ConfigOptionInt &rhs) const throw() { return this->value == rhs.value; } @@ -939,13 +980,25 @@ class ConfigOptionIntsTempl : public ConfigOptionVector bool operator==(const ConfigOptionIntsTempl &rhs) const throw() { return this->values == rhs.values; } bool operator< (const ConfigOptionIntsTempl &rhs) const throw() { return this->values < rhs.values; } // Could a special "nil" value be stored inside the vector, indicating undefined value? - bool nullable() const override { return NULLABLE; } + bool nullable() const override { return NULLABLE; } // Special "nil" value to be stored into the vector if this->supports_nil(). - static int32_t nil_value() { return std::numeric_limits::max(); } - // A scalar is nil, or all values of a vector are nil. - bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } - bool is_nil(size_t idx) const override { return idx < values.size() ? this->values[idx] == nil_value() : values.empty() ? this->default_value == nil_value() : this->values.front() == nil_value(); } - virtual double getFloat(int idx) const override { return values[idx]; } + static int32_t NIL_VALUE() { return std::numeric_limits::max(); } + int32_t get_int(int idx= 0) const override { return get_at(idx); } + double get_float(int idx = 0) const override { return get_at(idx); } + + bool is_nil(int idx = 0) const override + { + if (idx < 0) { + for (int32_t v : this->values) + if (v != NIL_VALUE()) + return false; + return true; + } else { + return idx < values.size() ? NIL_VALUE() == this->values[idx] : + values.empty() ? NIL_VALUE() == this->default_value : + NIL_VALUE() == this->values.front(); + } + } std::string serialize() const override { @@ -978,9 +1031,9 @@ class ConfigOptionIntsTempl : public ConfigOptionVector std::string item_str; while (std::getline(is, item_str, ',')) { boost::trim(item_str); - if (item_str == "nil") { + if (item_str == NIL_STR_VALUE) { if (NULLABLE) - this->values.push_back(nil_value()); + this->values.push_back(NIL_VALUE()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { @@ -995,9 +1048,9 @@ class ConfigOptionIntsTempl : public ConfigOptionVector private: void serialize_single_value(std::ostringstream &ss, const int32_t v) const { - if (v == nil_value()) { + if (v == NIL_VALUE()) { if (NULLABLE) - ss << "nil"; + ss << NIL_STR_VALUE; else throw ConfigurationError("Serializing NaN"); } else @@ -1058,7 +1111,7 @@ class ConfigOptionStrings : public ConfigOptionVector ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionStrings &rhs) const throw() { return this->values == rhs.values; } bool operator< (const ConfigOptionStrings &rhs) const throw() { return this->values < rhs.values; } - bool is_nil(size_t) const override { return false; } + bool is_nil(int) const override { return false; } std::string serialize() const override { @@ -1147,7 +1200,7 @@ class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl if (&v != &this->values.front()) ss << ","; this->serialize_single_value(ss, v); - if (! std::isnan(v)) + if (! (std::isnan(v) || v == NIL_VALUE())) ss << "%"; } std::string str = ss.str(); @@ -1161,7 +1214,7 @@ class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl for (const double v : this->values) { std::ostringstream ss; this->serialize_single_value(ss, v); - if (! std::isnan(v)) + if (! (std::isnan(v) || v == NIL_VALUE())) ss << "%"; vv.push_back(ss.str()); } @@ -1191,22 +1244,39 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent ConfigOptionType type() const override { return static_type(); } ConfigOption* clone() const override { return new ConfigOptionFloatOrPercent(*this); } ConfigOptionFloatOrPercent& operator=(const ConfigOption* opt) { this->set(opt); return *this; } - bool operator==(const ConfigOption &rhs) const override + bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionFloatOrPercent: Comparing incompatible types"); - assert(dynamic_cast(&rhs)); - return *this == *static_cast(&rhs); + assert(dynamic_cast(&rhs)); + return *this == *static_cast(&rhs); + } + bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw() + { + return this->value == rhs.value && this->percent == rhs.percent; + } + size_t hash() const throw() override + { + size_t seed = std::hash{}(this->value); + return this->percent ? seed ^ 0x9e3779b9 : seed; + } + bool operator<(const ConfigOptionFloatOrPercent &rhs) const throw() + { + return this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); } - bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw() - { return this->value == rhs.value && this->percent == rhs.percent; } - size_t hash() const throw() override - { size_t seed = std::hash{}(this->value); return this->percent ? seed ^ 0x9e3779b9 : seed; } - bool operator< (const ConfigOptionFloatOrPercent &rhs) const throw() - { return this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); } - double get_abs_value(double ratio_over) const - { return this->percent ? (ratio_over * this->value / 100) : this->value; } + double get_abs_value(double ratio_over) const + { + return this->percent ? (ratio_over * this->value / 100) : this->value; + } + // special case for get/set any: use a FloatOrPercent like for FloatsOrPercents, to have the is_percent + boost::any get_any(int idx = 0) const override { return boost::any(FloatOrPercent{value, percent}); } + void set_any(boost::any anyval, int idx = -1) override + { + auto fl_or_per = boost::any_cast(anyval); + this->value = fl_or_per.value; + this->percent = fl_or_per.percent; + } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) @@ -1264,10 +1334,7 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorsupports_nil(). - static FloatOrPercent nil_value() { return { std::numeric_limits::quiet_NaN(), false }; } - // A scalar is nil, or all values of a vector are nil. - bool is_nil() const override { for (auto v : this->values) if (! std::isnan(v.value)) return false; return true; } - bool is_nil(size_t idx) const override { return idx < values.size() ? std::isnan(this->values[idx].value) : values.empty() ? std::isnan(this->default_value.value) : std::isnan(this->values.front().value); } + static FloatOrPercent NIL_VALUE() { return FloatOrPercent{ std::numeric_limits::max(), false }; } double get_abs_value(size_t i, double ratio_over) const { if (this->is_nil(i)) return 0; const FloatOrPercent& data = this->get_at(i); @@ -1275,6 +1342,20 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorvalues) + if (v != NIL_VALUE()) + return false; + return true; + } else { + return idx < values.size() ? NIL_VALUE() == this->values[idx] : + values.empty() ? NIL_VALUE() == this->default_value : + NIL_VALUE() == this->values.front(); + } + } + std::string serialize() const override { std::ostringstream ss; @@ -1306,9 +1387,9 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorvalues.push_back(nil_value()); + this->values.push_back(NIL_VALUE()); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else { @@ -1334,9 +1415,9 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorvalue) && std::isnan(it2->value)) || *it1 == *it2)) + if (!(((std::isnan(it1->value) || it1->value == NIL_VALUE().value) && + (std::isnan(it2->value) || it2->value == NIL_VALUE().value)) || + *it1 == *it2)) return false; return true; } else @@ -1357,8 +1440,8 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector &v1, const std::vector &v2) { if (NULLABLE) { for (auto it1 = v1.begin(), it2 = v2.begin(); it1 != v1.end() && it2 != v2.end(); ++ it1, ++ it2) { - auto null1 = int(std::isnan(it1->value)); - auto null2 = int(std::isnan(it2->value)); + auto null1 = int(std::isnan(it1->value) || it1->value == NIL_VALUE().value); + auto null2 = int(std::isnan(it2->value) || it2->value == NIL_VALUE().value); return (null1 < null2) || (null1 == null2 && *it1 < *it2); } return v1.size() < v2.size(); @@ -1440,7 +1523,7 @@ class ConfigOptionPoints : public ConfigOptionVector bool operator==(const ConfigOptionPoints &rhs) const throw() { return this->values == rhs.values; } bool operator< (const ConfigOptionPoints &rhs) const throw() { return std::lexicographical_compare(this->values.begin(), this->values.end(), rhs.values.begin(), rhs.values.end(), [](const auto &l, const auto &r){ return l < r; }); } - bool is_nil(size_t) const override { return false; } + bool is_nil(int) const override { return false; } std::string serialize() const override { @@ -1549,7 +1632,9 @@ class ConfigOptionBool : public ConfigOptionSingle static ConfigOptionType static_type() { return coBool; } ConfigOptionType type() const override { return static_type(); } - bool getBool() const override { return this->value; } + bool get_bool(int idx = 0) const override { return this->value; } + int32_t get_int(int idx = 0) const override { return this->value?1:0; } + double get_float(int idx = 0) const override { return this->value?1.:0.; } ConfigOption* clone() const override { return new ConfigOptionBool(*this); } ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionBool &rhs) const throw() { return this->value == rhs.value; } @@ -1584,6 +1669,7 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector { public: ConfigOptionBoolsTempl() : ConfigOptionVector() {} + explicit ConfigOptionBoolsTempl(bool default_value) : ConfigOptionVector(default_value) {} explicit ConfigOptionBoolsTempl(size_t n, bool value) : ConfigOptionVector(n, (unsigned char)value) {} explicit ConfigOptionBoolsTempl(std::initializer_list il) { values.reserve(il.size()); for (bool b : il) values.emplace_back((unsigned char)b); } explicit ConfigOptionBoolsTempl(std::initializer_list il) { values.reserve(il.size()); for (unsigned char b : il) values.emplace_back(b); } @@ -1599,19 +1685,40 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector // Could a special "nil" value be stored inside the vector, indicating undefined value? bool nullable() const override { return NULLABLE; } // Special "nil" value to be stored into the vector if this->supports_nil(). - static unsigned char nil_value() { return std::numeric_limits::max(); } - // A scalar is nil, or all values of a vector are nil. - bool is_nil() const override { for (auto v : this->values) if (v != nil_value()) return false; return true; } - bool is_nil(size_t idx) const override { return idx < values.size() ? this->values[idx] == nil_value() : values.empty() ? this->default_value == nil_value() : this->values.front() == nil_value(); } - virtual double getFloat(int idx) const override { return values[idx] ? 1 : 0; } - - bool& get_at(size_t i) { - assert(! this->values.empty()); - return *reinterpret_cast(&((i < this->values.size()) ? this->values[i] : this->values.front())); - } + static unsigned char NIL_VALUE() { return std::numeric_limits::max(); } + bool get_bool(int idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0; } + int32_t get_int(int idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0 ? 1 : 0; } + double get_float(int idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0 ? 1. : 0.; } + + // I commented it, it shouldn't do anything wrong, as if(uint) == if(uint!=0) + //bool& get_at(size_t i) { + // assert(! this->values.empty()); + // if (this->values.empty()) { + // values.push_back(default_value); + // } + // return *reinterpret_cast(&((i < this->values.size()) ? this->values[i] : this->values.front())); + //} //FIXME this smells, the parent class has the method declared returning (unsigned char&). - bool get_at(size_t i) const { return ((i < this->values.size()) ? this->values[i] : this->values.front()) != 0; } + // I commented it, it shouldn't do anything wrong, as if(uint) == if(uint!=0) + // Please use get_bool(i) + //bool get_at(size_t i) const { + // return ((i < this->values.size()) ? this->values[i] : (this->values.empty() ? default_value != 0 : this->values.front() != 0)); + //} + + bool is_nil(int idx = 0) const override + { + if (idx < 0) { + for (uint8_t v : this->values) + if (v != NIL_VALUE()) + return false; + return true; + } else { + return idx < values.size() ? NIL_VALUE() == this->values[idx] : + values.empty() ? NIL_VALUE() == this->default_value : + NIL_VALUE() == this->values.front(); + } + } std::string serialize() const override { @@ -1645,9 +1752,9 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector while (std::getline(is, item_str, ',')) { boost::trim(item_str); unsigned char new_value = 0; - if (item_str == "nil") { + if (item_str == NIL_STR_VALUE) { if (NULLABLE) - new_value = nil_value(); + new_value = NIL_VALUE(); else throw ConfigurationError("Deserializing nil into a non-nullable object"); } else if (item_str == "1") { @@ -1671,9 +1778,9 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector protected: void serialize_single_value(std::ostringstream &ss, const unsigned char v) const { - if (v == nil_value()) { + if (v == NIL_VALUE()) { if (NULLABLE) - ss << "nil"; + ss << NIL_STR_VALUE; else throw ConfigurationError("Serializing NaN"); } else @@ -1707,22 +1814,25 @@ class ConfigOptionEnum : public ConfigOptionSingle ConfigOptionEnum& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionEnum &rhs) const throw() { return this->value == rhs.value; } bool operator< (const ConfigOptionEnum &rhs) const throw() { return int(this->value) < int(rhs.value); } - int32_t getInt() const override { return (int32_t)this->value; } - void setInt(int val) override { this->value = T(val); } + int32_t get_int(int idx = 0) const override { return (int32_t)this->value; } + void set_enum_int(int32_t val) override { this->value = T(val); } + // special case for get/set any: use a int like for ConfigOptionEnumGeneric, to simplify + boost::any get_any(int idx = 0) const override { return boost::any(get_int(idx)); } + void set_any(boost::any anyval, int idx = -1) override { set_enum_int(boost::any_cast(anyval)); } bool operator==(const ConfigOption &rhs) const override { if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionEnum: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum - return this->value == (T)rhs.getInt(); + return this->value == (T)rhs.get_int(); } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) throw ConfigurationError("ConfigOptionEnum: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum - this->value = (T)rhs->getInt(); + this->value = (T)rhs->get_int(); this->flags = rhs->flags; } @@ -1786,14 +1896,15 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionEnumGeneric: Comparing incompatible types"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum - return this->value == rhs.getInt(); + return this->value == rhs.get_int(); } + void set_enum_int(int32_t val) override { this->value = val; } void set(const ConfigOption *rhs) override { if (rhs->type() != this->type()) throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type"); // rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum - this->value = rhs->getInt(); + this->value = rhs->get_int(); this->flags = rhs->flags; } @@ -1839,7 +1950,7 @@ class ConfigOptionDef // Static text legend, // Vector value, but edited as a single string. - one_string, + // one_string, // it's now the default for vector without any idx. If you want to edit the first value, set the idx to 0 }; // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map. @@ -2391,6 +2502,7 @@ class DynamicConfig : public virtual ConfigBase // Be careful, as this method does not test the existence of opt_key in this->def(). bool set_key_value(const std::string &opt_key, ConfigOption *opt) { + assert(opt != nullptr); auto it = this->options.find(opt_key); if (it == this->options.end()) { this->options[opt_key].reset(opt); @@ -2423,10 +2535,15 @@ class DynamicConfig : public virtual ConfigBase int32_t& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } int32_t opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + // no dynamic_cast + bool get_bool(const t_config_option_key &opt_key, size_t idx = 0) const {return this->option(opt_key)->get_bool(idx);} + int32_t get_int(const t_config_option_key &opt_key, size_t idx = 0) const {return this->option(opt_key)->get_int(idx);} + double get_float(const t_config_option_key &opt_key, size_t idx = 0) const {return this->option(opt_key)->get_float(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. + // Thus the virtual method get_int() is used to retrieve the enum value. template - ENUM opt_enum(const t_config_option_key &opt_key) const { return static_cast(this->option(opt_key)->getInt()); } + ENUM opt_enum(const t_config_option_key &opt_key) const { return static_cast(this->option(opt_key)->get_int()); } 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; } @@ -2464,4 +2581,5 @@ class StaticConfig : public virtual ConfigBase } + #endif diff --git a/src/libslic3r/Extruder.cpp b/src/libslic3r/Extruder.cpp index 76b624e4334..ca156d5b8f5 100644 --- a/src/libslic3r/Extruder.cpp +++ b/src/libslic3r/Extruder.cpp @@ -31,6 +31,8 @@ Mill::Mill(uint16_t mill_id, GCodeConfig* config) : double Tool::extrude(double dE) { + assert(dE < std::numeric_limits::max()); + assert(dE > -std::numeric_limits::max()); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; @@ -48,8 +50,14 @@ double Tool::extrude(double dE) The restart_extra argument sets the extra length to be used for unretraction. If we're actually performing a retraction, any restart_extra value supplied will overwrite the previous one if any. */ -double Tool::retract(double length, double restart_extra, double restart_extra_toolchange) -{ +double Tool::retract(double length, std::optional restart_extra, std::optional restart_extra_toolchange) +{ + assert(length < std::numeric_limits::max()); + assert(length > 0); + assert(!restart_extra || *restart_extra < std::numeric_limits::max()); + assert(!restart_extra || *restart_extra > -std::numeric_limits::max()); + assert(!restart_extra_toolchange || *restart_extra_toolchange < std::numeric_limits::max()); + assert(!restart_extra_toolchange || *restart_extra_toolchange > -std::numeric_limits::max()); // in case of relative E distances we always reset to 0 before any output if (m_config->use_relative_e_distances) m_E = 0.; @@ -58,11 +66,11 @@ double Tool::retract(double length, double restart_extra, double restart_extra_t m_E -= to_retract; m_absolute_E -= to_retract; m_retracted += to_retract; - if(!std::isnan(restart_extra)) - m_restart_extra = restart_extra; + if(restart_extra) + m_restart_extra = *restart_extra; } - if (!std::isnan(restart_extra_toolchange)) - m_restart_extra_toolchange = restart_extra_toolchange; + if (restart_extra_toolchange) + m_restart_extra_toolchange = *restart_extra_toolchange; return to_retract; } @@ -204,11 +212,19 @@ double Extruder::retract_before_wipe() const double Extruder::retract_length() const { + assert(!m_config->retract_length.is_nil()); + assert(m_config->retract_length.get_at(m_id) < std::numeric_limits::max()); + assert(m_config->retract_length.get_at(m_id) > -std::numeric_limits::max()); + assert(m_config->retract_length.values.size() > m_id); return m_config->retract_length.get_at(m_id); } double Extruder::retract_lift() const { + assert(!m_config->retract_lift.is_nil()); + assert(m_config->retract_lift.get_at(m_id) < std::numeric_limits::max()); + assert(m_config->retract_lift.get_at(m_id) > -std::numeric_limits::max()); + assert(m_config->retract_lift.values.size() > m_id); return m_config->retract_lift.get_at(m_id); } @@ -225,6 +241,10 @@ int Extruder::deretract_speed() const double Extruder::retract_restart_extra() const { + assert(!m_config->retract_restart_extra.is_nil()); + assert(m_config->retract_restart_extra.get_at(m_id) < std::numeric_limits::max()); + assert(m_config->retract_restart_extra.get_at(m_id) > -std::numeric_limits::max()); + assert(m_config->retract_restart_extra.values.size() > m_id); return m_config->retract_restart_extra.get_at(m_id); } @@ -235,6 +255,10 @@ double Extruder::retract_length_toolchange() const double Extruder::retract_restart_extra_toolchange() const { + assert(!m_config->retract_restart_extra_toolchange.is_nil()); + assert(m_config->retract_restart_extra_toolchange.get_at(m_id) < std::numeric_limits::max()); + assert(m_config->retract_restart_extra_toolchange.get_at(m_id) > -std::numeric_limits::max()); + assert(m_config->retract_restart_extra_toolchange.values.size() > m_id); return m_config->retract_restart_extra_toolchange.get_at(m_id); } diff --git a/src/libslic3r/Extruder.hpp b/src/libslic3r/Extruder.hpp index 5e665f856d7..e4e16d2639e 100644 --- a/src/libslic3r/Extruder.hpp +++ b/src/libslic3r/Extruder.hpp @@ -25,7 +25,7 @@ class Tool uint16_t id() const { return m_id; } virtual double extrude(double dE); - virtual double retract(double length, double restart_extra, double restart_extra_from_toolchange); + virtual double retract(double length, std::optional restart_extra, std::optional restart_extra_from_toolchange); virtual double need_unretract(); virtual double unretract(); virtual void reset_retract(); diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 24ae880c3f2..63de0d2d51b 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -28,6 +28,8 @@ struct SurfaceFillParams : FillParams // double overlap = 0.; // Angle as provided by the region config, in radians. float angle = 0.f; + // If the region config allow, it's possible to rotate 90deg in odd layers + bool can_angle_cross =true; // Non-negative for a bridge. float bridge_angle = 0.f; BridgeType bridge_type = BridgeType::btFromNozzle; @@ -52,6 +54,7 @@ struct SurfaceFillParams : FillParams RETURN_COMPARE_NON_EQUAL(spacing); // RETURN_COMPARE_NON_EQUAL(overlap); RETURN_COMPARE_NON_EQUAL(angle); + RETURN_COMPARE_NON_EQUAL(can_angle_cross); RETURN_COMPARE_NON_EQUAL(density); RETURN_COMPARE_NON_EQUAL(monotonic); RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, connection); @@ -73,6 +76,7 @@ struct SurfaceFillParams : FillParams this->spacing == rhs.spacing && // this->overlap == rhs.overlap && this->angle == rhs.angle && + this->can_angle_cross == rhs.can_angle_cross && this->bridge_type == rhs.bridge_type && this->density == rhs.density && this->monotonic == rhs.monotonic && @@ -96,6 +100,23 @@ struct SurfaceFill { SurfaceFillParams params; }; +float compute_fill_angle(const PrintRegionConfig ®ion_config, size_t layer_id) +{ + float angle = 0; + if (!region_config.fill_angle_template.empty()) { + // fill pattern: replace fill angle + size_t idx = layer_id % region_config.fill_angle_template.values.size(); + angle = region_config.fill_angle_template.values[idx]; + } else { + angle = region_config.fill_angle.value; + } + angle += region_config.fill_angle_increment.value * layer_id; + // make compute in degre, then normalize and convert into rad. + angle = float(Geometry::deg2rad(angle)); + + return angle; +} + std::vector group_fills(const Layer &layer) { std::vector surface_fills; @@ -149,7 +170,7 @@ std::vector group_fills(const Layer &layer) params.pattern = region_config.bridge_fill_pattern.value; params.connection = region_config.infill_connection_bridge.value; } - if (region_config.infill_dense.getBool() + if (region_config.infill_dense.get_bool() && region_config.fill_density < 40 && surface.maxNbSolidLayersOnTop == 1) { params.density = 0.42f; @@ -182,14 +203,10 @@ std::vector group_fills(const Layer &layer) params.role = erSolidInfill; } } - params.fill_exactly = region_config.enforce_full_fill_volume.getBool(); + params.fill_exactly = region_config.enforce_full_fill_volume.get_bool(); params.bridge_angle = float(surface.bridge_angle); - if (is_denser) { - params.angle = 0; - } else { - params.angle = float(Geometry::deg2rad(region_config.fill_angle.value)); - params.angle += float(PI * (region_config.fill_angle_increment.value * layerm.layer()->id()) / 180.f); - } + params.angle = (is_denser) ? 0 : compute_fill_angle(region_config, layerm.layer()->id()); + params.can_angle_cross = region_config.fill_angle_cross; params.anchor_length = std::min(params.anchor_length, params.anchor_length_max); //adjust flow (to over-extrude when needed) @@ -368,7 +385,7 @@ std::vector group_fills(const Layer &layer) params.pattern = layerm.region().config().solid_fill_pattern.value; params.density = 100.f; params.role = erInternalInfill; - params.angle = float(Geometry::deg2rad(layerm.region().config().fill_angle.value)); + params.angle = compute_fill_angle(layerm.region().config(), layerm.layer()->id()); //FIXME FLOW decide what to use //params.flow = layerm.flow(frSolidInfill); // calculate the actual flow we'll be using for this infill @@ -479,6 +496,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: f->layer_id = this->id(); f->z = this->print_z; f->angle = surface_fill.params.angle; + f->can_angle_cross = surface_fill.params.can_angle_cross; f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; if (surface_fill.params.pattern == ipLightning) @@ -770,7 +788,9 @@ void Layer::make_ironing() ironing_params.line_spacing = config.ironing_spacing; ironing_params.height = default_layer_height * 0.01 * config.ironing_flowrate; ironing_params.speed = config.ironing_speed; - ironing_params.angle = config.ironing_angle <0 ? config.fill_angle * M_PI / 180. : config.ironing_angle * M_PI / 180.; + ironing_params.angle = config.ironing_angle <0 ? + compute_fill_angle(config, layerm->layer()->id()) : + float(Geometry::deg2rad(config.ironing_angle.value)); ironing_params.layerm = layerm; by_extruder.emplace_back(ironing_params); } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index bd70b6e4439..5dcadea8fc4 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -111,6 +111,8 @@ class Fill ExPolygons no_overlap_expolygons; // in radians, ccw, 0 = East float angle; + // to allow rectilinear to rotate 90deg on odd + float can_angle_cross; // In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines. // Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic. // If left to zero, the links will not be limited. @@ -156,6 +158,7 @@ class Fill overlap(0.), // Initial angle is undefined. angle(FLT_MAX), + can_angle_cross(true), link_max_length(0), loop_clipping(0), // The initial bounding box is empty, therefore undefined. @@ -181,7 +184,7 @@ class Fill BOOST_LOG_TRIVIAL(error) << "Error, the arachne fill isn't implemented"; }; - virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; } + virtual float _layer_angle(size_t idx) const { return can_angle_cross && (idx & 1) ? float(M_PI/2.) : 0; } virtual coord_t _line_spacing_for_density(float density) const; diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index e6812cbe097..f97eaaea3b2 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -149,7 +149,7 @@ void FillGyroid::_fill_surface_single( ExPolygon expolygon, Polylines &polylines_out) const { - float infill_angle = float(this->angle + (CorrectionAngle * 2 * M_PI) / 360.f); + float infill_angle = direction.first; if(std::abs(infill_angle) >= EPSILON) expolygon.rotate(-infill_angle); diff --git a/src/libslic3r/Fill/FillGyroid.hpp b/src/libslic3r/Fill/FillGyroid.hpp index 103100182cc..b235f1778c5 100644 --- a/src/libslic3r/Fill/FillGyroid.hpp +++ b/src/libslic3r/Fill/FillGyroid.hpp @@ -2,6 +2,7 @@ #define slic3r_FillGyroid_hpp_ #include "../libslic3r.h" +#include "../Geometry.hpp" #include "FillBase.hpp" @@ -13,10 +14,6 @@ class FillGyroid : public Fill FillGyroid() {} Fill* clone() const override { return new FillGyroid(*this); } - // Correction applied to regular infill angle to maximize printing - // speed in default configuration (degrees) - static constexpr float CorrectionAngle = -45.; - // Density adjustment to have a good %of weight. static constexpr double DensityAdjust = 2.44; @@ -25,6 +22,10 @@ class FillGyroid : public Fill protected: + // Correction applied to regular infill angle to maximize printing + // speed in default configuration (45 degrees) + float _layer_angle(size_t idx) const override { return this->can_angle_cross ? float(Geometry::deg2rad(-45.)) : 0.f; } + void _fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index dcc5464c0a6..aa9e27e37ab 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2945,7 +2945,7 @@ namespace Slic3r { log << "\n"; } if (config.option(opt_key) != nullptr && config.option(opt_key)->type() == ConfigOptionType::coEnum) { - log << "enum : " << config.option(opt_key)->getInt(); + log << "enum : " << config.option(opt_key)->get_int(); log << "\n"; const ConfigOptionDef* def = nullptr; try { @@ -2960,7 +2960,7 @@ namespace Slic3r { } } if (config.option(opt_key) != nullptr && config.option(opt_key)->type() == ConfigOptionType::coInt) { - log << "int : " << config.option(opt_key)->getInt(); + log << "int : " << config.option(opt_key)->get_int(); log << "\n"; } log.close(); @@ -3171,9 +3171,9 @@ namespace Slic3r { } if (obj->config.option(key) != nullptr && obj->config.option(key)->type() == ConfigOptionType::coEnum) { try{ - log << "raw_int_value : " << obj->config.option(key)->getInt() << "\n"; + log << "raw_int_value : " << obj->config.option(key)->get_int() << "\n"; } catch (std::exception ex) {} - log << "enum : " << obj->config.option(key)->getInt(); + log << "enum : " << obj->config.option(key)->get_int(); log << "\n"; const ConfigOptionDef* def = nullptr; try { @@ -3188,7 +3188,7 @@ namespace Slic3r { } } if (obj->config.option(key) != nullptr && obj->config.option(key)->type() == ConfigOptionType::coInt) { - log << "int : " << obj->config.option(key)->getInt(); + log << "int : " << obj->config.option(key)->get_int(); log << "\n"; } log.close(); @@ -3279,9 +3279,9 @@ namespace Slic3r { } if (volume->config.option(key) != nullptr && volume->config.option(key)->type() == ConfigOptionType::coEnum) { try{ - log << "raw_int_value : " << volume->config.option(key)->getInt() << "\n"; + log << "raw_int_value : " << volume->config.option(key)->get_int() << "\n"; } catch (std::exception ex) {} - log << "enum : " << volume->config.option(key)->getInt(); + log << "enum : " << volume->config.option(key)->get_int(); log << "\n"; const ConfigOptionDef* def = nullptr; try { @@ -3296,7 +3296,7 @@ namespace Slic3r { } } if (volume->config.option(key) != nullptr && volume->config.option(key)->type() == ConfigOptionType::coInt) { - log << "int : " << volume->config.option(key)->getInt(); + log << "int : " << volume->config.option(key)->get_int(); log << "\n"; } log.close(); diff --git a/src/libslic3r/Format/CWS.cpp b/src/libslic3r/Format/CWS.cpp index 395be504402..229bd64fa07 100644 --- a/src/libslic3r/Format/CWS.cpp +++ b/src/libslic3r/Format/CWS.cpp @@ -60,7 +60,7 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print) double used_material = (stats.objects_used_material + stats.support_used_material) / 1000; - int num_fade = print.default_object_config().faded_layers.getInt(); + int num_fade = print.default_object_config().faded_layers.get_int(); num_fade = num_fade >= 0 ? num_fade : 0; m["usedMaterial"] = std::to_string(used_material); diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 1f6b1aa90f0..deb70b25585 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -232,7 +232,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg) if (!opt_layerh || !opt_init_layerh) throw MissingProfileError("Invalid SL1 / SL1S file"); - return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; + return SliceParams{opt_layerh->get_float(), opt_init_layerh->get_float()}; } std::vector extract_slices_from_sla_archive( @@ -400,7 +400,7 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print) double used_material = (stats.objects_used_material + stats.support_used_material) / 1000; - int num_fade = print.default_object_config().faded_layers.getInt(); + int num_fade = print.default_object_config().faded_layers.get_int(); num_fade = num_fade >= 0 ? num_fade : 0; m["usedMaterial"] = std::to_string(used_material); diff --git a/src/libslic3r/Format/SLAArchive.cpp b/src/libslic3r/Format/SLAArchive.cpp index d6b77c41628..337c7f26158 100644 --- a/src/libslic3r/Format/SLAArchive.cpp +++ b/src/libslic3r/Format/SLAArchive.cpp @@ -25,15 +25,15 @@ std::unique_ptr SLAAbstractArchive::create_raster() const sla::RasterBase::PixelDim pxdim; std::array mirror; - double w = this->config().display_width.getFloat(); - double h = this->config().display_height.getFloat(); - auto pw = size_t(this->config().display_pixels_x.getInt()); - auto ph = size_t(this->config().display_pixels_y.getInt()); + double w = this->config().display_width.get_float(); + double h = this->config().display_height.get_float(); + auto pw = size_t(this->config().display_pixels_x.get_int()); + auto ph = size_t(this->config().display_pixels_y.get_int()); - mirror[X] = this->config().display_mirror_x.getBool(); - mirror[Y] = this->config().display_mirror_y.getBool(); + mirror[X] = this->config().display_mirror_x.get_bool(); + mirror[Y] = this->config().display_mirror_y.get_bool(); - auto ro = this->config().display_orientation.getInt(); + auto ro = this->config().display_orientation.get_int(); sla::RasterBase::Orientation orientation = ro == sla::RasterBase::roPortrait ? sla::RasterBase::roPortrait : sla::RasterBase::roLandscape; @@ -47,7 +47,7 @@ std::unique_ptr SLAAbstractArchive::create_raster() const pxdim = sla::RasterBase::PixelDim{w / pw, h / ph}; sla::RasterBase::Trafo tr{orientation, mirror}; - double gamma = this->config().gamma_correction.getFloat(); + double gamma = this->config().gamma_correction.get_float(); return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr); } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index a3d62f977f3..07d4e02c467 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -76,7 +76,7 @@ struct CoolingLine CoolingLine(unsigned int type, size_t line_start, size_t line_end) : type(type), line_start(line_start), line_end(line_end), - length(0.f), feedrate(0.f), time(0.f), time_max(0.f), slowdown(false) {} + length(0.f), has_move(false), feedrate(0.f), time(0.f), time_max(0.f), slowdown(false) {} bool adjustable(bool slowdown_external_perimeters) const { return (this->type & TYPE_ADJUSTABLE) && @@ -95,6 +95,8 @@ struct CoolingLine size_t line_end; // XY Euclidian length of this segment. float length; + // is this line move at least a coordinate (x, y, z, e, i,j) (iirc, it's everything but F) + bool has_move; // Current feedrate, possibly adjusted. float feedrate; // Current duration of this segment. @@ -436,6 +438,7 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') : (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : (*c == 'I') ? 5 : (*c == 'J') ? 6 : size_t(-1); + line.has_move = line.has_move || axis != 4; if (axis != size_t(-1)) { new_pos[axis] = float(atof(++c)); if (axis == 4) { @@ -565,8 +568,8 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } else if (boost::starts_with(sline, ";_EXTRUDETYPE_") && sline.size() > 14) { //set the extrusiontype line.type |= CoolingLine::Type(sline[14] - 'A') | CoolingLine::Type::TYPE_EXTRUDE_START; - assert(CoolingLine::to_extrusion_role(uint32_t(line.type)) != 0); - if (CoolingLine::to_extrusion_role(uint32_t(line.type)) == 0) { + assert(CoolingLine::to_extrusion_role(uint32_t(sline[14] - 'A')) != 0); + if (CoolingLine::to_extrusion_role(uint32_t(sline[14] - 'A')) == 0) { line.type |= ExtrusionRole::erCustom; } } else if (boost::starts_with(sline, "G4 ")) { @@ -854,14 +857,24 @@ std::string CoolingBuffer::apply_layer_cooldown( int fan_speeds[ExtrusionRole::erCount]; int default_fan_speed[ExtrusionRole::erCount]; #define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder) - int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); + const int min_fan_speed = m_config.fan_printer_min_speed; + assert(min_fan_speed >= 0); + int initial_default_fan_speed = EXTRUDER_CONFIG(default_fan_speed); + //if default_fan_speed activated, be sure it's at least the mins + if (initial_default_fan_speed > 0 && initial_default_fan_speed < min_fan_speed) + initial_default_fan_speed = min_fan_speed; + // 0 was deprecated, replaced by 1: allow 1 to still be 0 (and it's now deprecated) + if (initial_default_fan_speed == 1) + initial_default_fan_speed = 0; + //initialise the speed array for (int i = 0; i < ExtrusionRole::erCount; i++) { fan_control[i] = false; fan_speeds[i] = 0; - default_fan_speed[i] = min_fan_speed; + default_fan_speed[i] = initial_default_fan_speed; // 0 was deprecated, replaced by 1: allow 1 to still be 0 (and it's now deprecated) if (default_fan_speed[i] == 1) default_fan_speed[i] = 0; } + //set the fan controls default_fan_speed[ExtrusionRole::erBridgeInfill] = EXTRUDER_CONFIG(bridge_fan_speed); default_fan_speed[ExtrusionRole::erInternalBridgeInfill] = EXTRUDER_CONFIG(bridge_internal_fan_speed); default_fan_speed[ExtrusionRole::erTopSolidInfill] = EXTRUDER_CONFIG(top_fan_speed); @@ -875,8 +888,17 @@ std::string CoolingBuffer::apply_layer_cooldown( default_fan_speed[ExtrusionRole::erInternalInfill] = EXTRUDER_CONFIG(infill_fan_speed); default_fan_speed[ExtrusionRole::erOverhangPerimeter] = EXTRUDER_CONFIG(overhangs_fan_speed); default_fan_speed[ExtrusionRole::erGapFill] = EXTRUDER_CONFIG(gap_fill_fan_speed); + // if default is enabled, it takes over the settings that are disabled. + if (initial_default_fan_speed >= 0) { + for (int i = 0; i < ExtrusionRole::erCount; i++) { + // this setting is disbaled. As default is not, it will use the default value + if (default_fan_speed[i] < 0) { + default_fan_speed[i] = initial_default_fan_speed; + } + } + } auto change_extruder_set_fan = [this, layer_id, layer_time, &new_gcode, - &fan_control, &fan_speeds, &default_fan_speed, &min_fan_speed]() + &fan_control, &fan_speeds, &default_fan_speed, initial_default_fan_speed, min_fan_speed]() { int disable_fan_first_layers = EXTRUDER_CONFIG(disable_fan_first_layers); // Is the fan speed ramp enabled? @@ -888,8 +910,8 @@ std::string CoolingBuffer::apply_layer_cooldown( for (int i = 0; i < ExtrusionRole::erCount; i++) { fan_speeds[i] = default_fan_speed[i]; } - //if not always on, the default is "no fan" and not the min. - if (!EXTRUDER_CONFIG(fan_always_on)) { + //fan_speeds[0] carry the current default value. ensure it's not negative. + if (initial_default_fan_speed <= 0) { fan_speeds[0] = 0; } if (layer_time < slowdown_below_layer_time && fan_below_layer_time > 0) { @@ -922,13 +944,19 @@ std::string CoolingBuffer::apply_layer_cooldown( fan_speeds[idx] = std::clamp(int(float(fan_speeds[idx]) * factor + 0.01f), 0, 255); } } - //only activate fan control if the fan speed is higher than default - fan_control[0] = true; + //only activate fan control if the fan speed is higher than min + fan_control[0] = fan_speeds[0] >= 0; for (size_t i = 1; i < ExtrusionRole::erCount; i++) { - fan_control[i] = fan_speeds[i] >= 0 && fan_speeds[i] > fan_speeds[0]; + fan_control[i] = fan_speeds[i] >= 0; + } + + // if bridge_fan is disabled, it takes the value of default_fan + if (!fan_control[ExtrusionRole::erBridgeInfill] && fan_control[0]) { + fan_control[ExtrusionRole::erBridgeInfill] = true; + fan_speeds[ExtrusionRole::erBridgeInfill] = fan_speeds[0]; } - // if bridge_internal_fan is disabled, it takes the value of bridge_fan_control + // if bridge_internal_fan is disabled, it takes the value of bridge_fan if (!fan_control[ExtrusionRole::erInternalBridgeInfill] && fan_control[ExtrusionRole::erBridgeInfill]) { fan_control[ExtrusionRole::erInternalBridgeInfill] = true; fan_speeds[ExtrusionRole::erInternalBridgeInfill] = fan_speeds[ExtrusionRole::erBridgeInfill]; @@ -954,7 +982,13 @@ std::string CoolingBuffer::apply_layer_cooldown( fan_speeds[i] = 0; } } - if (fan_speeds[0] != m_fan_speed) { + // apply min fan speed, after the eventual speedup. + for (int i = 1; i < ExtrusionRole::erCount; i++) { + if (fan_control[i] && fan_speeds[i] > 0) { + fan_speeds[i] = std::max(fan_speeds[i], min_fan_speed); + } + } + if (fan_speeds[0] != m_fan_speed && fan_control[0]) { m_fan_speed = fan_speeds[0]; new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed, EXTRUDER_CONFIG(extruder_fan_offset), m_config.fan_percentage, @@ -965,7 +999,7 @@ std::string CoolingBuffer::apply_layer_cooldown( std::vector extrude_tree; const char *pos = gcode.c_str(); int current_feedrate = 0; - int stored_fan_speed = m_fan_speed; + int stored_fan_speed = m_fan_speed < 0 ? 0 : m_fan_speed; change_extruder_set_fan(); for (const CoolingLine *line : lines) { const char *line_start = gcode.c_str() + line->line_start; @@ -983,7 +1017,7 @@ std::string CoolingBuffer::apply_layer_cooldown( new_gcode.append(line_start, line_end - line_start); } } else if (line->type & CoolingLine::TYPE_STORE_FOR_WT) { - stored_fan_speed = m_fan_speed; + stored_fan_speed = m_fan_speed < 0 ? 0 : m_fan_speed; } else if (line->type & CoolingLine::TYPE_RESTORE_AFTER_WT) { new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, stored_fan_speed, EXTRUDER_CONFIG(extruder_fan_offset), m_config.fan_percentage, @@ -994,8 +1028,12 @@ std::string CoolingBuffer::apply_layer_cooldown( fan_need_set = true; } else if (line->type & CoolingLine::TYPE_EXTRUDE_END) { assert(extrude_tree.size() > 0); - extrude_tree.pop_back(); - fan_need_set = true; + if (extrude_tree.size() > 0) { + new_gcode.append(std::string(";TYPE_EXTRUDE remove type ") + + ExtrusionEntity::role_to_string(extrude_tree.back()) + std::string("\n")); + extrude_tree.pop_back(); + fan_need_set = true; + } } else if (line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_ADJUSTABLE_EMPTY | CoolingLine::TYPE_ADJUSTABLE_MAYBE | CoolingLine::TYPE_WIPE | CoolingLine::TYPE_HAS_F)) { // Find the start of a comment, or roll to the end of line. @@ -1012,7 +1050,7 @@ std::string CoolingBuffer::apply_layer_cooldown( new_feedrate = line->slowdown ? int(floor(60. * line->feedrate + 0.5)) : atoi(fpos); if (new_feedrate == current_feedrate) { // No need to change the F value. - if ((line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_ADJUSTABLE_EMPTY | CoolingLine::TYPE_ADJUSTABLE_MAYBE | CoolingLine::TYPE_WIPE)) || line->length == 0.) + if ((line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_ADJUSTABLE_EMPTY | CoolingLine::TYPE_ADJUSTABLE_MAYBE | CoolingLine::TYPE_WIPE)) || !line->has_move ) // Feedrate does not change and this line does not move the print head. Skip the complete G-code line including the G-code comment. end = line_end; else @@ -1092,10 +1130,10 @@ std::string CoolingBuffer::apply_layer_cooldown( } } if (!fan_set) { - //return to default - new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed, - EXTRUDER_CONFIG(extruder_fan_offset), m_config.fan_percentage, - "set default fan"); + // return to default + new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed < 0 ? 0 : m_fan_speed, + EXTRUDER_CONFIG(extruder_fan_offset), m_config.fan_percentage, + "set default fan"); } fan_need_set = false; } diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index 1d1808073e1..0043e32e5a0 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -52,7 +52,7 @@ class CoolingBuffer { uint16_t m_num_extruders { 0 }; // Referencs GCode::m_config, which is FullPrintConfig. While the PrintObjectConfig slice of FullPrintConfig is being modified, // the PrintConfig slice of FullPrintConfig is constant, thus no thread synchronization is required. - const PrintConfig &m_config; + const FullPrintConfig &m_config; uint16_t m_current_extruder; //saved previous unslowed layer diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index dd3fc8909c0..c204563dd9b 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -637,14 +637,16 @@ std::string GCodeWriter::retract(bool before_wipe) return this->_retract( factor * config_region->print_retract_length, factor * m_tool->retract_restart_extra(), - NAN, + std::nullopt, "retract" ); } + auto rect_length = m_tool->retract_length(); + auto rect_length2 = m_tool->retract_restart_extra(); return this->_retract( factor * m_tool->retract_length(), factor * m_tool->retract_restart_extra(), - NAN, + std::nullopt, "retract" ); } @@ -655,13 +657,13 @@ std::string GCodeWriter::retract_for_toolchange(bool before_wipe) assert(factor >= 0. && factor <= 1. + EPSILON); return this->_retract( factor * m_tool->retract_length_toolchange(), - NAN, + std::nullopt, factor * m_tool->retract_restart_extra_toolchange(), "retract for toolchange" ); } -std::string GCodeWriter::_retract(double length, double restart_extra, double restart_extra_toolchange, const std::string &comment) +std::string GCodeWriter::_retract(double length, std::optional restart_extra, std::optional restart_extra_toolchange, const std::string &comment) { std::ostringstream gcode; @@ -675,9 +677,17 @@ std::string GCodeWriter::_retract(double length, double restart_extra, double re if (this->config.use_volumetric_e) { double d = m_tool->filament_diameter(); double area = d * d * PI/4; + assert(length * area < std::numeric_limits::max()); + assert(length * area > 0); + assert(!restart_extra || *restart_extra * area < std::numeric_limits::max()); + assert(!restart_extra || *restart_extra * area > -std::numeric_limits::max()); + assert(!restart_extra_toolchange || *restart_extra_toolchange * area < std::numeric_limits::max()); + assert(!restart_extra_toolchange || *restart_extra_toolchange * area > -std::numeric_limits::max()); length = length * area; - restart_extra = restart_extra * area; - restart_extra_toolchange = restart_extra_toolchange * area; + if(restart_extra) + restart_extra = *restart_extra * area; + if(restart_extra_toolchange) + restart_extra_toolchange = *restart_extra_toolchange * area; } double dE = m_tool->retract(length, restart_extra, restart_extra_toolchange); diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 7d46974a7e1..a4f8d525c2c 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -115,7 +115,7 @@ class GCodeWriter { Vec3d m_pos = Vec3d::Zero(); std::string _travel_to_z(double z, const std::string &comment); - std::string _retract(double length, double restart_extra, double restart_extra_toolchange, const std::string &comment); + std::string _retract(double length, std::optional restart_extra, std::optional restart_extra_toolchange, const std::string &comment); }; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index fd2078787ca..798b1710bc7 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1752,9 +1752,9 @@ int ModelVolume::extruder_id() const int extruder_id = -1; if (this->is_model_part()) { const ConfigOption *opt = this->config.option("extruder"); - if ((opt == nullptr) || (opt->getInt() == 0)) + if ((opt == nullptr) || (opt->get_int() == 0)) opt = this->object->config.option("extruder"); - extruder_id = (opt == nullptr) ? 0 : opt->getInt(); + extruder_id = (opt == nullptr) ? 0 : opt->get_int(); } return extruder_id; } diff --git a/src/libslic3r/MultiMaterialSegmentation.cpp b/src/libslic3r/MultiMaterialSegmentation.cpp index f6a380d1a41..dc1448cb808 100644 --- a/src/libslic3r/MultiMaterialSegmentation.cpp +++ b/src/libslic3r/MultiMaterialSegmentation.cpp @@ -1691,7 +1691,7 @@ std::vector> multi_material_segmentation_by_painting(con std::vector edge_grids(num_layers); const ConstLayerPtrsAdaptor layers = print_object.layers(); std::vector input_expolygons(num_layers); - coord_t resolution = scale_t(print_object.config().option("resolution")->getFloat()); + coord_t resolution = scale_t(print_object.config().option("resolution")->get_float()); throw_on_cancel_callback(); diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index b13d689dba8..92c25b503e0 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -770,7 +770,7 @@ namespace client if (vector_opt->is_extruder_size()) { if (raw_opt->type() == coFloats || raw_opt->type() == coInts || raw_opt->type() == coBools) - return vector_opt->getFloat(int(current_extruder_id)); + return vector_opt->get_float(int(current_extruder_id)); if (raw_opt->type() == coFloatsOrPercents) { const ConfigOptionFloatsOrPercents* opt_fl_per = static_cast(raw_opt); if (!opt_fl_per->values[current_extruder_id].percent) @@ -869,7 +869,7 @@ namespace client ctx->throw_exception("Variable does not exist", opt_key); if (opt_index->type() != coInt) ctx->throw_exception("Indexing variable has to be integer", opt_key); - int idx = opt_index->getInt(); + int idx = opt_index->get_int(); if (idx < 0) ctx->throw_exception("Negative vector index", opt_key); output = vec->vserialize()[(idx >= (int)vec->size()) ? 0 : idx]; @@ -936,12 +936,12 @@ namespace client } const ConfigOptionDef* opt_def; switch (opt.opt->type()) { - case coFloat: output.set_d(opt.opt->getFloat()); break; - case coInt: output.set_i(opt.opt->getInt()); break; + case coFloat: output.set_d(opt.opt->get_float()); break; + case coInt: output.set_i(opt.opt->get_int()); break; case coString: output.set_s(static_cast(opt.opt)->value); break; - case coPercent: output.set_d(opt.opt->getFloat()); break; + case coPercent: output.set_d(opt.opt->get_float()); break; case coPoint: output.set_s(opt.opt->serialize()); break; - case coBool: output.set_b(opt.opt->getBool()); break; + case coBool: output.set_b(opt.opt->get_bool()); break; case coFloatOrPercent: { if (boost::ends_with(opt_key, "extrusion_width")) { @@ -949,12 +949,12 @@ namespace client output.set_d(Flow::extrusion_width(opt_key, *ctx, static_cast(ctx->current_extruder_id))); } else if (! static_cast(opt.opt)->percent) { // Not a percent, just return the value. - output.set_d(opt.opt->getFloat()); + output.set_d(opt.opt->get_float()); } else { // Resolve dependencies using the "ratio_over" link to a parent value. opt_def = print_config_def.get(opt_key); assert(opt_def != nullptr); - double v = opt.opt->getFloat() * 0.01; // percent to ratio + double v = opt.opt->get_float() * 0.01; // percent to ratio for (;;) { const ConfigOption *opt_parent = opt_def->ratio_over.empty() ? nullptr : ctx->resolve_symbol(opt_def->ratio_over); if (opt_parent == nullptr) @@ -985,7 +985,7 @@ namespace client case coInts: opt_def = print_config_def.get(opt_key); if (opt_def->is_vector_extruder) { - output.set_i(int(((ConfigOptionVectorBase*)opt.opt)->getFloat(int(ctx->current_extruder_id)))); + output.set_i(int(((ConfigOptionVectorBase *) opt.opt)->get_float(int(ctx->current_extruder_id)))); break; } else ctx->throw_exception("Unknown scalar variable type", opt.it_range); @@ -993,7 +993,7 @@ namespace client case coPercents: vector_opt = static_cast(opt.opt); if (vector_opt->is_extruder_size()) { - output.set_d(((ConfigOptionVectorBase*)opt.opt)->getFloat(int(ctx->current_extruder_id))); + output.set_d(((ConfigOptionVectorBase *) opt.opt)->get_float(int(ctx->current_extruder_id))); break; } else ctx->throw_exception("Unknown scalar variable type", opt.it_range); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 60c4ab3e0d3..df9e639bc84 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -507,7 +507,9 @@ static std::vector s_Preset_print_options { "ironing_spacing", "ironing_angle", "fill_angle", + "fill_angle_cross", "fill_angle_increment", + "fill_angle_template", "bridge_angle", "solid_infill_below_area", "only_retract_when_crossing_perimeters", "enforce_retract_first_layer", @@ -745,10 +747,11 @@ static std::vector s_Preset_filament_options { "filament_dip_insertion_speed", "filament_dip_extraction_speed", //skinnydip params end "temperature", "first_layer_temperature", "bed_temperature", "first_layer_bed_temperature", - // cooling - "fan_always_on", - "min_fan_speed", - "max_fan_speed", + // "cooling", + // "fan_always_on", (now default_fan_speed) + // "min_fan_speed", (now fan_printer_min_speed) + "default_fan_speed", + "max_fan_speed", "bridge_fan_speed", "bridge_internal_fan_speed", "external_perimeter_fan_speed", @@ -805,6 +808,7 @@ static std::vector s_Preset_printer_options { "fan_speedup_overhangs", "fan_speedup_time", "fan_percentage", + "fan_printer_min_speed", "gcode_filename_illegal_char", "gcode_flavor", "gcode_precision_xyz", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index ecf364e8b8b..75e588ede42 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -77,6 +77,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "complete_objects_sort", "cooling", "default_acceleration", + "default_fan_speed", "deretract_speed", "disable_fan_first_layers", "duplicate_distance", @@ -94,12 +95,12 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "extruder_fan_offset" "extruder_temperature_offset", "extrusion_multiplier", - "fan_always_on", "fan_below_layer_time", "fan_kickstart", "fan_speedup_overhangs", "fan_speedup_time", "fan_percentage", + "fan_printer_min_speed", "filament_colour", "filament_custom_variables", "filament_diameter", @@ -134,7 +135,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "max_print_height", "max_print_speed", "max_volumetric_speed", - "min_fan_speed", "min_length", "min_print_speed", "milling_toolchange_end_gcode", diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 1c62773d053..7d577f64f0a 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -159,7 +159,7 @@ static bool layer_height_ranges_equal(const t_layer_config_ranges &lr1, const t_ const auto &kvp2 = *it2 ++; if (std::abs(kvp1.first.first - kvp2.first.first ) > EPSILON || std::abs(kvp1.first.second - kvp2.first.second) > EPSILON || - (check_layer_height && std::abs(kvp1.second.option("layer_height")->getFloat() - kvp2.second.option("layer_height")->getFloat()) > EPSILON)) + (check_layer_height && std::abs(kvp1.second.option("layer_height")->get_float() - kvp2.second.option("layer_height")->get_float()) > EPSILON)) return false; } return true; diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index e5f299b22ff..035fc3334a2 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -76,7 +76,8 @@ std::string PrintBase::output_filename(const std::string &format, const std::str cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base)); } try { - uint16_t extruder_initial = config_override->option("initial_extruder") != nullptr && config_override->option("initial_extruder")->type() == coInt ? config_override->option("initial_extruder")->getInt() : 0; + uint16_t extruder_initial = config_override->option("initial_extruder") != nullptr && + config_override->option("initial_extruder")->type() == coInt ? config_override->option("initial_extruder")->get_int() : 0; boost::filesystem::path filepath = format.empty() ? cfg.opt_string("input_filename_base") + default_ext : this->placeholder_parser().process(format, extruder_initial, &cfg); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 1d8700dd169..a81ec9343d2 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -376,7 +376,6 @@ void PrintConfigDef::init_common_params() def->mode = comExpert | comPrusa; def->min = 0; def->max = 2048; - //def->gui_type = ConfigOptionDef::GUIType::one_string; // i prefer two boxes def->set_default_value(new ConfigOptionPoints{ std::initializer_list{ Vec2d(0,0), Vec2d(0,0) } }); def = this->add("thumbnails_color", coString); @@ -729,7 +728,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Bridges fan speed"); def->category = OptionCategory::cooling; def->tooltip = L("This fan speed is enforced during bridges and overhangs. It won't slow down the fan if it's currently running at a higher speed." - "\nSet to -1 to disable this override (Bridge will use default fan speed)." + "\nSet to -1 to disable this override (Bridges will use default fan speed)." "\nCan be disabled by disable_fan_first_layers and increased by low layer time."); def->sidetext = L("%"); def->min = -1; @@ -742,8 +741,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Infill bridges fan speed"); def->category = OptionCategory::cooling; def->tooltip = L("This fan speed is enforced during all infill bridges. It won't slow down the fan if it's currently running at a higher speed." - "\nSet to 1 to follow default speed." - "\nSet to -1 to disable this override (internal bridges will use Bridges fan speed)." + "\nSet to -1 to disable this override (Internal bridges will use Bridges fan speed)." "\nCan be disabled by disable_fan_first_layers and increased by low layer time."); def->sidetext = L("%"); def->min = -1; @@ -1212,6 +1210,20 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionStrings()); def->cli = ConfigOptionDef::nocli; + def = this->add("default_fan_speed", coInts); + def->label = L("Default fan speed"); + def->category = OptionCategory::cooling; + def->tooltip = L( + "Default speed for the fan, to set the speed for features where there is no fan control. Useful for PLA and other low-temp filament." + "\nSet 0 to disable the fan by default. Useful for ABS and other high-temp filaments." + "\nSet -1 to disable. if disabled, the beahavior isn't defined yet. The goal is to avoid adding fan speed commands."); + def->mode = comSimpleAE | comSuSi; + def->min = -1; + def->max = 100; + def->is_vector_extruder = true; + def->set_default_value(new ConfigOptionInts({-1})); + def->aliases = { "min_fan_speed" }; // only if "fan_always_on" + def = this->add("default_print_profile", coString); def->label = L("Default print profile"); def->tooltip = L("Default print profile associated with the current printer profile. " @@ -1815,6 +1827,8 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comSuSi; def->set_default_value(new ConfigOptionFloatOrPercent(0, false, false)); +#if 0 + //not used anymore, to remove !! @DEPRECATED (replaces by default_fan_speed) def = this->add("fan_always_on", coBools); def->label = L("Keep fan always on"); def->category = OptionCategory::cooling; @@ -1823,6 +1837,7 @@ void PrintConfigDef::init_fff_params() def->mode = comSimpleAE | comPrusa; def->is_vector_extruder = true; def->set_default_value(new ConfigOptionBools{ true }); +#endif def = this->add("fan_below_layer_time", coFloats); def->label = L("Enable fan if layer print time is below"); @@ -2260,6 +2275,13 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comPrusa; def->set_default_value(new ConfigOptionFloat(45)); + def = this->add("fill_angle_cross", coBool); + def->label = L("Alternate Fill Angle"); + def->category = OptionCategory::infill; + def->tooltip = L("It's better for some infill like rectilinear to rotate 90° each layer. If this settign is deactivated, they won't do that anymore."); + def->mode = comAdvancedE | comSuSi; + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("fill_angle_increment", coFloat); def->label = L("Fill"); def->full_label = L("Fill angle increment"); @@ -2273,6 +2295,22 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comSuSi; def->set_default_value(new ConfigOptionFloat(0)); + def = this->add("fill_angle_template", coFloats); + def->label = L("Fill angle pattern"); + def->full_label = L("Fill angle pattern"); + def->category = OptionCategory::infill; + def->tooltip = L("This define the succetion of infill angle. When defined, it replaces the fill_angle" + ", and there won't be any extra 90° for each layer added, but the fill_angle_increment will still be used." + " The first layer start with the first angle. If a new pattern is used in a modifier" + ", it will choose the layer angle from the pattern as if it has started from the first layer." + "Empty this settings to disable and recover the old behavior."); + def->sidetext = L("°"); + def->min = 0; + def->max = 360; + def->full_width = true; + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionFloats(0.)); + def = this->add("fill_density", coPercent); def->gui_type = ConfigOptionDef::GUIType::f_enum_open; def->gui_flags = "show_value"; @@ -3650,6 +3688,8 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comPrusa; def->set_default_value(new ConfigOptionFloat(0)); +#if 0 + // replaced by fan_printer_min_speed, to remove !! @DEPRECATED def = this->add("min_fan_speed", coInts); def->label = L("Default fan speed"); def->full_label = L("Default fan speed"); @@ -3661,6 +3701,7 @@ void PrintConfigDef::init_fff_params() def->mode = comSimpleAE | comPrusa; def->is_vector_extruder = true; def->set_default_value(new ConfigOptionInts{ 35 }); +#endif def = this->add("fan_percentage", coBool); def->label = L("Fan PWM from 0-100"); @@ -4095,7 +4136,7 @@ void PrintConfigDef::init_fff_params() "\nset 0 to disable"); def->sidetext = L("mm or %"); def->mode = comExpert | comSuSi; - def->set_default_value(new ConfigOptionFloatOrPercent(false, 0)); + def->set_default_value(new ConfigOptionFloatOrPercent(0, false)); def = this->add("perimeter_fan_speed", coInts); def->label = L("Internal Perimeter fan speed"); @@ -4194,7 +4235,6 @@ void PrintConfigDef::init_fff_params() "and they can access the Slic3r config settings by reading environment variables." "\nThe script, if passed as a relative path, will also be searched from the slic3r directory, " "the slic3r configuration directory and the user directory."); - def->gui_flags = "serialized"; def->multiline = true; def->full_width = true; def->height = 6; @@ -4220,6 +4260,17 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvancedE | comSuSi; def->set_default_value(new ConfigOptionString{ "" }); + def = this->add("fan_printer_min_speed", coInt); + def->label = L("Minimum fan speed"); + def->full_label = L("Minimum fan speed"); + def->category = OptionCategory::general; + def->tooltip = L("This setting represents the minimum fan speed (like minimum PWM) your fan needs to work."); + def->sidetext = L("%"); + def->min = 0; + def->max = 100; + def->mode = comSimpleAE | comSuSi; + def->set_default_value(new ConfigOptionInt(35)); + def = this->add("printer_model", coString); def->label = L("Printer type"); def->tooltip = L("Type of the printer."); @@ -6394,16 +6445,39 @@ void PrintConfigDef::init_fff_params() auto it_opt = options.find(opt_key); assert(it_opt != options.end()); def = this->add_nullable(std::string("filament_") + opt_key, it_opt->second.type); - def->label = it_opt->second.label; + def->label = it_opt->second.label; def->full_label = it_opt->second.full_label; - def->tooltip = it_opt->second.tooltip; + def->tooltip = it_opt->second.tooltip; def->sidetext = it_opt->second.sidetext; def->mode = it_opt->second.mode; + // create default value with the default value is taken from the default value of the config. + // put a nil value as first entry. switch (def->type) { - case coFloats : def->set_default_value(new ConfigOptionFloatsNullable (static_cast(it_opt->second.default_value.get())->values)); break; - case coPercents : def->set_default_value(new ConfigOptionPercentsNullable(static_cast(it_opt->second.default_value.get())->values)); break; - case coFloatsOrPercents : def->set_default_value(new ConfigOptionFloatsOrPercentsNullable(static_cast(it_opt->second.default_value.get())->values)); break; - case coBools : def->set_default_value(new ConfigOptionBoolsNullable (static_cast(it_opt->second.default_value.get())->values)); break; + case coBools: { + ConfigOptionBoolsNullable* opt = new ConfigOptionBoolsNullable(it_opt->second.default_value.get()->get_bool()); + opt->set_at(ConfigOptionBoolsNullable::NIL_VALUE(), 0); + def->set_default_value(opt); + break; + } + case coFloats: { + ConfigOptionFloatsNullable *opt = new ConfigOptionFloatsNullable(it_opt->second.default_value.get()->get_float()); + opt->set_any(ConfigOptionFloatsNullable::create_nil(), 0); + def->set_default_value(opt); + break; + } + case coPercents: { + ConfigOptionPercentsNullable *opt = new ConfigOptionPercentsNullable(it_opt->second.default_value.get()->get_float()); + opt->set_any(ConfigOptionFloatsNullable::create_nil(), 0); + def->set_default_value(opt); + break; + } + case coFloatsOrPercents: { + ConfigOptionFloatsOrPercentsNullable*opt = new ConfigOptionFloatsOrPercentsNullable( + static_cast(it_opt->second.default_value.get())->get_at(0)); + opt->set_at(ConfigOptionFloatsOrPercentsNullable::NIL_VALUE(), 0); + def->set_default_value(opt); + break; + } default: assert(false); } } @@ -7595,6 +7669,16 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va if (boost::starts_with(opt_key, "thin_perimeters") && value == "1") value = "100%"; + if ("fan_always_on" == opt_key) { + if (value != "1") { + //min_fan_speed is already converted to default_fan_speed, just has to deactivate it if not always_on + opt_key = "default_fan_speed"; // note: maybe this doesn't works, as default_fan_speed can also get its value from min_fan_speed + value = "0"; + } else { + opt_key = ""; + } + } + //prusa if ("gcode_flavor" == opt_key) { if ("reprap" == value) @@ -7667,6 +7751,11 @@ std::map PrintConfigDef::from_prusa(t_config_option_key value = "rectilinear"; output["fill_angle_increment"] = "90"; } + if ("fan_always_on" == opt_key) { + //min_fan_speed is already converted to default_fan_speed, just has to deactivate it if not always_on + if (value != "1") + output["default_fan_speed"] = "0"; + } return output; } @@ -7738,6 +7827,7 @@ std::unordered_set prusa_export_to_remove_keys = { "curve_smoothing_angle_convex", "curve_smoothing_cutoff_dist", "curve_smoothing_precision", +//"default_fan_speed", used to convert to min_fan_speed & fan_always_on "default_speed", "enforce_full_fill_volume", "exact_last_layer_height", @@ -7757,6 +7847,7 @@ std::unordered_set prusa_export_to_remove_keys = { "extrusion_spacing", "fan_kickstart", "fan_percentage", +"fan_printer_min_speed", "fan_speedup_overhangs", "fan_speedup_time", "feature_gcode", @@ -7940,7 +8031,7 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& } else if ("monotonicgapfill" == value) { value = "monotonic"; } - if (all_conf.has("fill_angle_increment") && ((int(all_conf.option("fill_angle_increment")->getFloat())-90)%180) == 0 && "rectilinear" == value + if (all_conf.has("fill_angle_increment") && ((int(all_conf.option("fill_angle_increment")->get_float())-90)%180) == 0 && "rectilinear" == value && ("fill_pattern" == opt_key || "top_fill_pattern" == opt_key)) { value = "alignedrectilinear"; } @@ -8075,6 +8166,16 @@ std::map PrintConfigDef::to_prusa(t_config_option_key& if ("bed_custom_texture" == opt_key || "Bed custom texture" == opt_key) { value = Slic3r::find_full_path(value, value).generic_string(); } + if ("default_fan_speed" == opt_key) { + if (value == "0") { + opt_key = "min_fan_speed"; + value = all_conf.option("fan_printer_min_speed")->get_float(); + new_entries["fan_always_on"] = "0"; + } else { + opt_key = "min_fan_speed"; + new_entries["fan_always_on"] = "1"; + } + } return new_entries; } @@ -8103,10 +8204,10 @@ PrinterTechnology printer_technology(const ConfigBase& cfg) if (opt) return opt->value; const ConfigOptionBool* export_opt = cfg.option("export_sla"); - if (export_opt && export_opt->getBool()) return ptSLA; + if (export_opt && export_opt->get_bool()) return ptSLA; export_opt = cfg.option("export_gcode"); - if (export_opt && export_opt->getBool()) return ptFFF; + if (export_opt && export_opt->get_bool()) return ptFFF; return ptUnknown; } @@ -8162,7 +8263,7 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) double base_dist = 0; //std::cout << "START min_object_distance =>" << base_dist << "\n"; const ConfigOptionBool* co_opt = config->option("complete_objects"); - if (config->option("parallel_objects_step")->getFloat() > 0 || co_opt && co_opt->value) { + if (config->option("parallel_objects_step")->get_float() > 0 || co_opt && co_opt->value) { double skirt_dist = 0; try { std::vector vals = dynamic_cast(config->option("nozzle_diameter"))->values; @@ -8172,7 +8273,7 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) // min object distance is max(duplicate_distance, clearance_radius) // /2 becasue we only count the grawing for the current object //add 1 as safety offset. - double extruder_clearance_radius = config->option("extruder_clearance_radius")->getFloat() / 2; + double extruder_clearance_radius = config->option("extruder_clearance_radius")->get_float() / 2; if (extruder_clearance_radius > base_dist) { base_dist = extruder_clearance_radius; } @@ -8181,15 +8282,15 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) //ideally, we should use print::first_layer_height() const double first_layer_height = dynamic_cast(config->option("first_layer_height"))->get_abs_value(max_nozzle_diam); //add the skirt - int skirts = config->option("skirts")->getInt(); + int skirts = config->option("skirts")->get_int(); if (skirts > 0 && ref_height == 0) - skirts += config->option("skirt_brim")->getInt(); - if (skirts > 0 && config->option("skirt_height")->getInt() >= 1 && !config->option("complete_objects_one_skirt")->getBool()) { + skirts += config->option("skirt_brim")->get_int(); + if (skirts > 0 && config->option("skirt_height")->get_int() >= 1 && !config->option("complete_objects_one_skirt")->get_bool()) { float overlap_ratio = 1; //can't know the extruder, so we settle on the worst: 100% //if (config->option("filament_max_overlap")) overlap_ratio = config->get_computed_value("filament_max_overlap"); if (ref_height == 0) { - skirt_dist = config->option("skirt_distance")->getFloat(); + skirt_dist = config->option("skirt_distance")->get_float(); Flow skirt_flow = Flow::new_from_config_width( frPerimeter, *Flow::extrusion_width_option("skirt", *config), @@ -8204,9 +8305,9 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) //set to 0 becasue it's incorporated into the base_dist, so we don't want to be added in to it again. skirt_dist = 0; } else { - double skirt_height = ((double)config->option("skirt_height")->getInt() - 1) * config->get_computed_value("layer_height") + first_layer_height; + double skirt_height = ((double)config->option("skirt_height")->get_int() - 1) * config->get_computed_value("layer_height") + first_layer_height; if (ref_height <= skirt_height) { - skirt_dist = config->option("skirt_distance")->getFloat(); + skirt_dist = config->option("skirt_distance")->get_float(); Flow skirt_flow = Flow::new_from_config_width( frPerimeter, *Flow::extrusion_width_option("skirt", *config), @@ -8232,13 +8333,13 @@ double min_object_distance(const ConfigBase *config, double ref_height /* = 0*/) void DynamicPrintConfig::normalize_fdm() { if (this->has("extruder")) { - int extruder = this->option("extruder")->getInt(); + int extruder = this->option("extruder")->get_int(); this->erase("extruder"); if (extruder != 0) { if (!this->has("infill_extruder")) - this->option("infill_extruder", true)->setInt(extruder); + this->option("infill_extruder", true)->value = (extruder); if (!this->has("perimeter_extruder")) - this->option("perimeter_extruder", true)->setInt(extruder); + this->option("perimeter_extruder", true)->value = (extruder); // Don't propagate the current extruder to support. // For non-soluble supports, the default "0" extruder means to use the active extruder, // for soluble supports one certainly does not want to set the extruder to non-soluble. @@ -8252,7 +8353,7 @@ void DynamicPrintConfig::normalize_fdm() this->erase("first_layer_extruder"); if (!this->has("solid_infill_extruder") && this->has("infill_extruder")) - this->option("solid_infill_extruder", true)->setInt(this->option("infill_extruder")->getInt()); + this->option("solid_infill_extruder", true)->value = (this->option("infill_extruder")->get_int()); if (this->has("spiral_vase") && this->opt("spiral_vase", true)->value) { { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 24949305e2c..2fd8d4d6150 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -828,7 +828,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionBool, only_one_perimeter_top)) ((ConfigOptionBool, only_one_perimeter_top_other_algo)) ((ConfigOptionFloat, fill_angle)) + ((ConfigOptionBool, fill_angle_cross)) ((ConfigOptionFloat, fill_angle_increment)) + ((ConfigOptionFloats, fill_angle_template)) ((ConfigOptionPercent, fill_density)) ((ConfigOptionEnum, fill_pattern)) ((ConfigOptionPercent, first_layer_flow_ratio)) @@ -1000,8 +1002,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloats, extruder_temperature_offset)) ((ConfigOptionString, extrusion_axis)) ((ConfigOptionFloats, extrusion_multiplier)) - ((ConfigOptionBool, fan_percentage)) ((ConfigOptionFloat, fan_kickstart)) + ((ConfigOptionBool, fan_percentage)) + ((ConfigOptionInt, fan_printer_min_speed)) ((ConfigOptionBool, fan_speedup_overhangs)) ((ConfigOptionFloat, fan_speedup_time)) ((ConfigOptionFloats, filament_cost)) @@ -1048,7 +1051,8 @@ PRINT_CONFIG_CLASS_DEFINE( // r - regular expression // i - case insensitive // w - whole word - ((ConfigOptionStrings, gcode_substitutions)) ((ConfigOptionString, layer_gcode)) + ((ConfigOptionStrings, gcode_substitutions)) + ((ConfigOptionString, layer_gcode)) ((ConfigOptionString, feature_gcode)) ((ConfigOptionFloat, max_gcode_per_second)) ((ConfigOptionFloatOrPercent, max_print_speed)) @@ -1146,6 +1150,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( //((ConfigOptionBools, cooling)) ((ConfigOptionFloatOrPercent, default_acceleration)) ((ConfigOptionInts, disable_fan_first_layers)) + ((ConfigOptionInts, default_fan_speed)) ((ConfigOptionEnum, draft_shield)) ((ConfigOptionFloat, duplicate_distance)) ((ConfigOptionBool, enforce_retract_first_layer)) @@ -1155,7 +1160,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionFloat, extruder_clearance_radius)) ((ConfigOptionStrings, extruder_colour)) ((ConfigOptionPoints, extruder_offset)) - ((ConfigOptionBools, fan_always_on)) + //((ConfigOptionBools, fan_always_on)) ((ConfigOptionFloats, fan_below_layer_time)) ((ConfigOptionStrings, filament_colour)) ((ConfigOptionStrings, filament_custom_variables)) @@ -1184,7 +1189,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionStrings, milling_toolchange_start_gcode)) //((ConfigOptionPoints, milling_offset)) //((ConfigOptionFloats, milling_z_offset)) - ((ConfigOptionInts, min_fan_speed)) + //((ConfigOptionInts, min_fan_speed)) // now fan_printer_min_speed ((ConfigOptionFloatsOrPercents, min_layer_height)) ((ConfigOptionFloats, min_print_speed)) ((ConfigOptionFloat, min_skirt_length)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 32762b3424c..da8375dd980 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -957,7 +957,9 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "solid_fill_pattern" || opt_key == "enforce_full_fill_volume" || opt_key == "fill_angle" + || opt_key == "fill_angle_cross" || opt_key == "fill_angle_increment" + || opt_key == "fill_angle_template" || opt_key == "fill_top_flow_ratio" || opt_key == "fill_smooth_width" || opt_key == "fill_smooth_distribution" @@ -1295,7 +1297,7 @@ bool PrintObject::invalidate_state_by_config_options( for (const PrintRegion* region : this->m_print->print_regions_mutable()) { //count how many surface there are on each one - if (region->config().infill_dense.getBool() && region->config().fill_density < 40) { + if (region->config().infill_dense.get_bool() && region->config().fill_density < 40) { std::vector layeridx2lregion; std::vector new_surfaces; //surface store, as you can't modify them when working in // // store the LayerRegion on which we are working @@ -2539,12 +2541,12 @@ static constexpr const std::initializer_list keys_extrud // 2) Copy the rest of the values. for (auto it = in.cbegin(); it != in.cend(); ++it) if (it->first != key_extruder) - if (ConfigOption* my_opt = out.option(it->first, false); my_opt != nullptr) { + if (ConfigOptionInt *my_opt = out.option(it->first, false); my_opt != nullptr) { if (one_of(it->first, keys_extruders)) { // Ignore "default" extruders. - int extruder = static_cast(it->second.get())->value; + int extruder = it->second->get_int(); if (extruder > 0) - my_opt->setInt(extruder); + my_opt->value = (extruder); } else my_opt->set(it->second.get()); } diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index 196646dc9ee..042c0fab346 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -195,8 +195,8 @@ XYRotation from_transform3f(const Transform3f &tr) inline bool is_on_floor(const SLAPrintObjectConfig &cfg) { - auto opt_elevation = cfg.support_object_elevation.getFloat(); - auto opt_padaround = cfg.pad_around_object.getBool(); + auto opt_elevation = cfg.support_object_elevation.get_float(); + auto opt_padaround = cfg.pad_around_object.get_bool(); return opt_elevation < EPSILON || opt_padaround; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 69b80fbf62d..d7b53a4325e 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -30,7 +30,7 @@ namespace Slic3r { bool is_zero_elevation(const SLAPrintObjectConfig &c) { - return c.pad_enable.getBool() && c.pad_around_object.getBool(); + return c.pad_enable.get_bool() && c.pad_around_object.get_bool(); } // Compile the argument for support creation from the static print config. @@ -38,20 +38,20 @@ sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) { sla::SupportTreeConfig scfg; - scfg.enabled = c.supports_enable.getBool(); - scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat(); - double pillar_r = 0.5 * c.support_pillar_diameter.getFloat(); + scfg.enabled = c.supports_enable.get_bool(); + scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.get_float(); + double pillar_r = 0.5 * c.support_pillar_diameter.get_float(); scfg.head_back_radius_mm = pillar_r; scfg.head_fallback_radius_mm = - 0.01 * c.support_small_pillar_diameter_percent.getFloat() * pillar_r; - scfg.head_penetration_mm = c.support_head_penetration.getFloat(); - scfg.head_width_mm = c.support_head_width.getFloat(); + 0.01 * c.support_small_pillar_diameter_percent.get_float() * pillar_r; + scfg.head_penetration_mm = c.support_head_penetration.get_float(); + scfg.head_width_mm = c.support_head_width.get_float(); scfg.object_elevation_mm = is_zero_elevation(c) ? - 0. : c.support_object_elevation.getFloat(); - scfg.bridge_slope = c.support_critical_angle.getFloat() * PI / 180.0 ; - scfg.max_bridge_length_mm = c.support_max_bridge_length.getFloat(); - scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.getFloat(); - switch(c.support_pillar_connection_mode.getInt()) { + 0. : c.support_object_elevation.get_float(); + scfg.bridge_slope = c.support_critical_angle.get_float() * PI / 180.0 ; + scfg.max_bridge_length_mm = c.support_max_bridge_length.get_float(); + scfg.max_pillar_link_distance_mm = c.support_max_pillar_link_distance.get_float(); + switch(c.support_pillar_connection_mode.get_int()) { case slapcmZigZag: scfg.pillar_connection_mode = sla::PillarConnectionMode::zigzag; break; case slapcmCross: @@ -59,15 +59,15 @@ sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c) case slapcmDynamic: scfg.pillar_connection_mode = sla::PillarConnectionMode::dynamic; break; } - scfg.ground_facing_only = c.support_buildplate_only.getBool(); - scfg.pillar_widening_factor = c.support_pillar_widening_factor.getFloat(); - scfg.base_radius_mm = 0.5*c.support_base_diameter.getFloat(); - scfg.base_height_mm = c.support_base_height.getFloat(); + scfg.ground_facing_only = c.support_buildplate_only.get_bool(); + scfg.pillar_widening_factor = c.support_pillar_widening_factor.get_float(); + scfg.base_radius_mm = 0.5*c.support_base_diameter.get_float(); + scfg.base_height_mm = c.support_base_height.get_float(); scfg.pillar_base_safety_distance_mm = - c.support_base_safety_distance.getFloat() < EPSILON ? - scfg.safety_distance_mm : c.support_base_safety_distance.getFloat(); + c.support_base_safety_distance.get_float() < EPSILON ? + scfg.safety_distance_mm : c.support_base_safety_distance.get_float(); - scfg.max_bridges_on_pillar = unsigned(c.support_max_bridges_on_pillar.getInt()); + scfg.max_bridges_on_pillar = unsigned(c.support_max_bridges_on_pillar.get_int()); return scfg; } @@ -79,12 +79,12 @@ sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c) ret.enabled = is_zero_elevation(c); if(ret.enabled) { - ret.everywhere = c.pad_around_object_everywhere.getBool(); - ret.object_gap_mm = c.pad_object_gap.getFloat(); - ret.stick_width_mm = c.pad_object_connector_width.getFloat(); - ret.stick_stride_mm = c.pad_object_connector_stride.getFloat(); + ret.everywhere = c.pad_around_object_everywhere.get_bool(); + ret.object_gap_mm = c.pad_object_gap.get_float(); + ret.stick_width_mm = c.pad_object_connector_width.get_float(); + ret.stick_stride_mm = c.pad_object_connector_stride.get_float(); ret.stick_penetration_mm = c.pad_object_connector_penetration - .getFloat(); + .get_float(); } return ret; @@ -94,12 +94,12 @@ sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c) { sla::PadConfig pcfg; - pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat(); - pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0; + pcfg.wall_thickness_mm = c.pad_wall_thickness.get_float(); + pcfg.wall_slope = c.pad_wall_slope.get_float() * PI / 180.0; - pcfg.max_merge_dist_mm = c.pad_max_merge_distance.getFloat(); - pcfg.wall_height_mm = c.pad_wall_height.getFloat(); - pcfg.brim_size_mm = c.pad_brim_size.getFloat(); + pcfg.max_merge_dist_mm = c.pad_max_merge_distance.get_float(); + pcfg.wall_height_mm = c.pad_wall_height.get_float(); + pcfg.brim_size_mm = c.pad_brim_size.get_float(); // set builtin pad implicitly ON pcfg.embed_object = builtin_pad_cfg(c); @@ -620,7 +620,7 @@ std::pair SLAPrint::validate(std:: for(SLAPrintObject * po : m_objects) { const ModelObject *mo = po->model_object(); - bool supports_en = po->config().supports_enable.getBool(); + bool supports_en = po->config().supports_enable.get_bool(); if(supports_en && mo->sla_points_status == sla::PointsStatus::UserModified && @@ -653,16 +653,16 @@ std::pair SLAPrint::validate(std:: if (!pval.empty()) return { PrintBase::PrintValidationError::pveWrongSettings, pval }; } - double expt_max = m_printer_config.max_exposure_time.getFloat(); - double expt_min = m_printer_config.min_exposure_time.getFloat(); - double expt_cur = m_material_config.exposure_time.getFloat(); + double expt_max = m_printer_config.max_exposure_time.get_float(); + double expt_min = m_printer_config.min_exposure_time.get_float(); + double expt_cur = m_material_config.exposure_time.get_float(); if (expt_cur < expt_min || expt_cur > expt_max) return { PrintBase::PrintValidationError::pveWrongSettings, L("Exposition time is out of printer profile bounds.") }; - double iexpt_max = m_printer_config.max_initial_exposure_time.getFloat(); - double iexpt_min = m_printer_config.min_initial_exposure_time.getFloat(); - double iexpt_cur = m_material_config.initial_exposure_time.getFloat(); + double iexpt_max = m_printer_config.max_initial_exposure_time.get_float(); + double iexpt_min = m_printer_config.min_initial_exposure_time.get_float(); + double iexpt_cur = m_material_config.initial_exposure_time.get_float(); if (iexpt_cur < iexpt_min || iexpt_cur > iexpt_max) return { PrintBase::PrintValidationError::pveWrongSettings, L("Initial exposition time is out of printer profile bounds.") }; @@ -1031,11 +1031,11 @@ bool SLAPrintObject::invalidate_all_steps() double SLAPrintObject::get_elevation() const { if (is_zero_elevation(m_config)) return 0.; - bool en = m_config.supports_enable.getBool(); + bool en = m_config.supports_enable.get_bool(); - double ret = en ? m_config.support_object_elevation.getFloat() : 0.; + double ret = en ? m_config.support_object_elevation.get_float() : 0.; - if(m_config.pad_enable.getBool()) { + if(m_config.pad_enable.get_bool()) { // Normally the elevation for the pad itself would be the thickness of // its walls but currently it is half of its thickness. Whatever it // will be in the future, we provide the config to the get_pad_elevation @@ -1057,7 +1057,7 @@ double SLAPrintObject::get_current_elevation() const if(!has_supports && !has_pad) return 0; else if(has_supports && !has_pad) { - return m_config.support_object_elevation.getFloat(); + return m_config.support_object_elevation.get_float(); } return get_elevation(); @@ -1148,7 +1148,7 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const const TriangleMesh& SLAPrintObject::support_mesh() const { - if(m_config.supports_enable.getBool() && m_supportdata) + if(m_config.supports_enable.get_bool() && m_supportdata) return m_supportdata->tree_mesh; return EMPTY_MESH; @@ -1156,7 +1156,7 @@ const TriangleMesh& SLAPrintObject::support_mesh() const const TriangleMesh& SLAPrintObject::pad_mesh() const { - if(m_config.pad_enable.getBool() && m_supportdata) + if(m_config.pad_enable.get_bool() && m_supportdata) return m_supportdata->pad_mesh; return EMPTY_MESH; @@ -1165,7 +1165,7 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const { if (m_hollowing_data && m_hollowing_data->interior && - m_config.hollowing_enable.getBool()) + m_config.hollowing_enable.get_bool()) return sla::get_mesh(*m_hollowing_data->interior); return EMPTY_TRIANGLE_SET; diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 0a0b5015c5f..261d74894dc 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -78,7 +78,7 @@ SLAPrint::Steps::Steps(SLAPrint *print) : m_print{print} , m_rng{std::random_device{}()} , objcount{m_print->m_objects.size()} - , ilhd{m_print->m_material_config.initial_layer_height.getFloat()} + , ilhd{m_print->m_material_config.initial_layer_height.get_float()} , ilh{float(ilhd)} , ilhs{scaled(ilhd)} , objectstep_scale{(max_objstatus - min_objstatus) / (objcount * 100.0)} @@ -88,11 +88,11 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin { if (o == soSupport && !po.m_supportdata) return; - auto faded_lyrs = size_t(po.m_config.faded_layers.getInt()); - double min_w = m_print->m_printer_config.elephant_foot_min_width.getFloat() / 2.; - double start_efc = m_print->m_printer_config.first_layer_size_compensation.getFloat(); + auto faded_lyrs = size_t(po.m_config.faded_layers.get_int()); + double min_w = m_print->m_printer_config.elephant_foot_min_width.get_float() / 2.; + double start_efc = m_print->m_printer_config.first_layer_size_compensation.get_float(); - double doffs = m_print->m_printer_config.absolute_correction.getFloat(); + double doffs = m_print->m_printer_config.absolute_correction.get_float(); coord_t clpr_offs = scaled(doffs); faded_lyrs = std::min(po.m_slice_index.size(), faded_lyrs); @@ -123,16 +123,16 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) { po.m_hollowing_data.reset(); - if (! po.m_config.hollowing_enable.getBool()) { + if (! po.m_config.hollowing_enable.get_bool()) { BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; return; } BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!"; - double thickness = po.m_config.hollowing_min_thickness.getFloat(); - double quality = po.m_config.hollowing_quality.getFloat(); - double closing_d = po.m_config.hollowing_closing_distance.getFloat(); + double thickness = po.m_config.hollowing_min_thickness.get_float(); + double quality = po.m_config.hollowing_quality.get_float(); + double closing_d = po.m_config.hollowing_closing_distance.get_float(); sla::HollowingConfig hlwcfg{thickness, quality, closing_d}; sla::InteriorPtr interior = generate_interior(po.transformed_mesh(), hlwcfg); @@ -490,7 +490,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) // We need to prepare the slice index... - double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat(); + double lhd = m_print->m_objects.front()->m_config.layer_height.get_float(); float lh = float(lhd); coord_t lhs = scaled(lhd); auto && bb3d = mesh.bounding_box(); @@ -567,7 +567,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) // We apply the printer correction offset here. apply_printer_corrections(po, soModel); - if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) + if(po.m_config.supports_enable.get_bool() || po.m_config.pad_enable.get_bool()) { po.m_supportdata.reset(new SLAPrintObject::SupportData(po.get_mesh_to_print())); } @@ -578,7 +578,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) void SLAPrint::Steps::support_points(SLAPrintObject &po) { // If supports are disabled, we can skip the model scan. - if(!po.m_config.supports_enable.getBool()) return; + if(!po.m_config.supports_enable.get_bool()) return; if (!po.m_supportdata) po.m_supportdata.reset(new SLAPrintObject::SupportData(po.get_mesh_to_print())); @@ -681,7 +681,7 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) po.m_supportdata->create_support_tree(ctl); - if (!po.m_config.supports_enable.getBool()) return; + if (!po.m_config.supports_enable.get_bool()) return; throw_if_canceled(); @@ -706,7 +706,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // and before the supports had been sliced. (or the slicing has to be // repeated) - if(po.m_config.pad_enable.getBool()) { + if(po.m_config.pad_enable.get_bool()) { // Get the distilled pad configuration from the config sla::PadConfig pcfg = make_pad_cfg(po.m_config); @@ -714,13 +714,13 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { double pad_h = pcfg.full_height(); const TriangleMesh &trmesh = po.transformed_mesh(); - if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { + if (!po.m_config.supports_enable.get_bool() || pcfg.embed_object) { // No support (thus no elevation) or zero elevation mode // we sometimes call it "builtin pad" is enabled so we will // get a sample from the bottom of the mesh and use it for pad // creation. sla::pad_blueprint(trmesh.its, bp, float(pad_h), - float(po.m_config.layer_height.getFloat()), + float(po.m_config.layer_height.get_float()), [this](){ throw_if_canceled(); }); } @@ -748,7 +748,7 @@ void SLAPrint::Steps::slice_supports(SLAPrintObject &po) { if(sd) sd->support_slices.clear(); // Don't bother if no supports and no pad is present. - if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool()) + if (!po.m_config.supports_enable.get_bool() && !po.m_config.pad_enable.get_bool()) return; if(sd && sd->support_tree_ptr) { @@ -887,18 +887,18 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { print_statistics.clear(); - const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); - const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; - const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; - const double hv_tilt = printer_config.high_viscosity_tilt_time.getFloat();// 10.0; + const double area_fill = printer_config.area_fill.get_float()*0.01;// 0.5 (50%); + const double fast_tilt = printer_config.fast_tilt_time.get_float();// 5.0; + const double slow_tilt = printer_config.slow_tilt_time.get_float();// 8.0; + const double hv_tilt = printer_config.high_viscosity_tilt_time.get_float();// 10.0; - const double init_exp_time = material_config.initial_exposure_time.getFloat(); - const double exp_time = material_config.exposure_time.getFloat(); + const double init_exp_time = material_config.initial_exposure_time.get_float(); + const double exp_time = material_config.exposure_time.get_float(); - const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20] + const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.get_int();// 10 // [3;20] - const auto width = scaled(printer_config.display_width.getFloat()); - const auto height = scaled(printer_config.display_height.getFloat()); + const auto width = scaled(printer_config.display_width.get_float()); + const auto height = scaled(printer_config.display_height.get_float()); const double display_area = width*height; double supports_volume(0.0); diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index 9e84279e6fa..844d40bb8b1 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -315,7 +315,7 @@ std::vector layer_height_profile_from_ranges( for (t_layer_config_ranges::const_iterator it_range = layer_config_ranges.begin(); it_range != layer_config_ranges.end(); ++ it_range) { coordf_t lo = it_range->first.first; coordf_t hi = std::min(it_range->first.second, slicing_params.object_print_z_height()); - coordf_t height = it_range->second.option("layer_height")->getFloat(); + coordf_t height = it_range->second.option("layer_height")->get_float(); if (! ranges_non_overlapping.empty()) // Trim current low with the last high. lo = std::max(lo, ranges_non_overlapping.back().first.second); diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index 2cf116e3118..ec877bd4c14 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -42,7 +42,7 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para t_config_option_key key; switch (param) { case Parameter::RectSize: - def.type = coPoints; + def.type = coPoint; def.set_default_value(new ConfigOptionPoints{ Vec2d(200, 200) }); def.min = 0; def.max = 100000; @@ -51,7 +51,7 @@ void BedShape::append_option_line(ConfigOptionsGroupShp optgroup, Parameter para key = "rect_size"; break; case Parameter::RectOrigin: - def.type = coPoints; + def.type = coPoint; def.set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) }); def.min = -100000; def.max = 100000; @@ -120,12 +120,14 @@ void BedShape::apply_optgroup_values(ConfigOptionsGroupShp optgroup) { switch (m_build_volume.type()) { case BuildVolume::Type::Circle: - optgroup->set_value("diameter", double_to_string(2. * unscaled(m_build_volume.circle().radius))); + optgroup->set_value("diameter", 2. * unscaled(m_build_volume.circle().radius)); break; default: // rectangle, convex, concave... - optgroup->set_value("rect_size" , new ConfigOptionPoints{ to_2d(m_build_volume.bounding_volume().size()) }); - optgroup->set_value("rect_origin" , new ConfigOptionPoints{ - to_2d(m_build_volume.bounding_volume().min) }); + optgroup->set_value("rect_size", Vec2d(m_build_volume.bounding_volume().size().x(), + m_build_volume.bounding_volume().size().y())); + optgroup->set_value("rect_origin", Vec2d(-m_build_volume.bounding_volume().min.x(), + -m_build_volume.bounding_volume().min.y())); } } diff --git a/src/slic3r/GUI/CalibrationBridgeDialog.cpp b/src/slic3r/GUI/CalibrationBridgeDialog.cpp index 476b78bdb58..0d703d42915 100644 --- a/src/slic3r/GUI/CalibrationBridgeDialog.cpp +++ b/src/slic3r/GUI/CalibrationBridgeDialog.cpp @@ -133,7 +133,7 @@ void CalibrationBridgeDialog::create_geometry(std::string setting_to_test, bool DynamicPrintConfig new_print_config = *print_config; //make a copy new_print_config.set_key_value("complete_objects", new ConfigOptionBool(true)); //if skirt, use only one - if (print_config->option("skirts")->getInt() > 0 && print_config->option("skirt_height")->getInt() > 0) { + if (print_config->option("skirts")->get_int() > 0 && print_config->option("skirt_height")->get_int() > 0) { new_print_config.set_key_value("complete_objects_one_skirt", new ConfigOptionBool(true)); } diff --git a/src/slic3r/GUI/CalibrationFlowDialog.cpp b/src/slic3r/GUI/CalibrationFlowDialog.cpp index 040c7a79cee..d697aad6f20 100644 --- a/src/slic3r/GUI/CalibrationFlowDialog.cpp +++ b/src/slic3r/GUI/CalibrationFlowDialog.cpp @@ -132,7 +132,7 @@ void CalibrationFlowDialog::create_geometry(float start, float delta) { DynamicPrintConfig new_print_config = *print_config; //make a copy new_print_config.set_key_value("complete_objects", new ConfigOptionBool(true)); //if skirt, use only one - if (print_config->option("skirts")->getInt() > 0 && print_config->option("skirt_height")->getInt() > 0) { + if (print_config->option("skirts")->get_int() > 0 && print_config->option("skirt_height")->get_int() > 0) { new_print_config.set_key_value("complete_objects_one_skirt", new ConfigOptionBool(true)); } diff --git a/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp b/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp index 344be6ba129..699f1e861c5 100644 --- a/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp +++ b/src/slic3r/GUI/CalibrationOverBridgeDialog.cpp @@ -93,8 +93,8 @@ void CalibrationOverBridgeDialog::create_geometry(bool over_bridge) { bool has_to_arrange = false; const ConfigOptionFloat* extruder_clearance_radius = print_config->option("extruder_clearance_radius"); const ConfigOptionPoints* bed_shape = printer_config->option("bed_shape"); - const float brim_width = print_config->option("brim_width")->getFloat(); - const float skirt_width = print_config->option("skirts")->getInt() == 0 ? 0 : print_config->option("skirt_distance")->getFloat() + print_config->option("skirts")->getInt() * nozzle_diameter * 2; + const float brim_width = print_config->option("brim_width")->get_float(); + const float skirt_width = print_config->option("skirts")->get_int() == 0 ? 0 : print_config->option("skirt_distance")->get_float() + print_config->option("skirts")->get_int() * nozzle_diameter * 2; Vec2d bed_size = BoundingBoxf(bed_shape->values).size(); Vec2d bed_min = BoundingBoxf(bed_shape->values).min; float offsetx = 3 + 30 * xyz_scale + extruder_clearance_radius->value + brim_width + (brim_width > extruder_clearance_radius->value ? brim_width - extruder_clearance_radius->value : 0); @@ -114,7 +114,8 @@ void CalibrationOverBridgeDialog::create_geometry(bool over_bridge) { DynamicPrintConfig new_print_config = *print_config; //make a copy new_print_config.set_key_value("complete_objects", new ConfigOptionBool(true)); //if skirt, use only one - if (print_config->option("skirts")->getInt() > 0 && print_config->option("skirt_height")->getInt() > 0) { + if (print_config->option("skirts")->get_int() > 0 && + print_config->option("skirt_height")->get_int() > 0) { new_print_config.set_key_value("complete_objects_one_skirt", new ConfigOptionBool(true)); } diff --git a/src/slic3r/GUI/CalibrationRetractionDialog.cpp b/src/slic3r/GUI/CalibrationRetractionDialog.cpp index 5604513cfe2..3f9dd45eae4 100644 --- a/src/slic3r/GUI/CalibrationRetractionDialog.cpp +++ b/src/slic3r/GUI/CalibrationRetractionDialog.cpp @@ -81,14 +81,13 @@ void CalibrationRetractionDialog::remove_slowdown(wxCommandEvent& event_args) { DynamicPrintConfig new_filament_config = *filament_config; //make a copy const ConfigOptionFloats *fil_conf = filament_config->option("slowdown_below_layer_time"); - ConfigOptionFloats *new_fil_conf = new ConfigOptionFloats(); - new_fil_conf->default_value = 5; + ConfigOptionFloats *new_fil_conf = new ConfigOptionFloats(5); new_fil_conf->values = fil_conf->values; new_fil_conf->values[0] = 0; new_filament_config.set_key_value("slowdown_below_layer_time", new_fil_conf); - fil_conf = filament_config->option("fan_below_layer_time"); new_fil_conf = new ConfigOptionFloats(); - new_fil_conf->default_value = 60; + fil_conf = filament_config->option("fan_below_layer_time"); + new_fil_conf = new ConfigOptionFloats(60); new_fil_conf->values = fil_conf->values; new_fil_conf->values[0] = 0; new_filament_config.set_key_value("fan_below_layer_time", new_fil_conf); @@ -253,7 +252,7 @@ void CalibrationRetractionDialog::create_geometry(wxCommandEvent& event_args) { DynamicPrintConfig new_print_config = *print_config; //make a copy new_print_config.set_key_value("complete_objects", new ConfigOptionBool(true)); //if skirt, use only one - if (print_config->option("skirts")->getInt() > 0 && print_config->option("skirt_height")->getInt() > 0) { + if (print_config->option("skirts")->get_int() > 0 && print_config->option("skirt_height")->get_int() > 0) { new_print_config.set_key_value("complete_objects_one_skirt", new ConfigOptionBool(true)); } this->gui_app->get_tab(Preset::TYPE_FFF_PRINT)->load_config(new_print_config); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 006c9387fed..4e7b0e51838 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -86,9 +86,9 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con && config->opt_bool("overhangs_reverse") == false && config->opt_bool("gap_fill_last") == false && config->opt_int("solid_over_perimeters") == 0 - && config->option("seam_notch_all")->getFloat() == 0 - && config->option("seam_notch_inner")->getFloat() == 0 - && config->option("seam_notch_outer")->getFloat() == 0 + && config->option("seam_notch_all")->get_float() == 0 + && config->option("seam_notch_inner")->get_float() == 0 + && config->option("seam_notch_outer")->get_float() == 0 )) { wxString msg_text = _(L("The Spiral Vase mode requires:\n" "- no top solid layers\n" @@ -363,7 +363,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("overhangs_reverse_threshold", have_perimeters && config->opt_bool("overhangs_reverse")); toggle_field("overhangs_speed_enforce", have_perimeters && !config->opt_bool("perimeter_loop")); toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top") && !have_arachne); - toggle_field("thin_perimeters_all", have_perimeters && config->option("thin_perimeters")->getFloat() != 0 && !have_arachne); + toggle_field("thin_perimeters_all", have_perimeters && config->option("thin_perimeters")->get_float() != 0 && !have_arachne); bool have_thin_wall = !have_arachne && have_perimeters; toggle_field("thin_walls", have_thin_wall); for (auto el : { "thin_walls_min_width", "thin_walls_overlap", "thin_walls_merge" }) @@ -374,9 +374,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("perimeter_loop_seam", config->opt_bool("perimeter_loop")); - bool have_notch = have_perimeters && (config->option("seam_notch_all")->getFloat() != 0 || - config->option("seam_notch_inner")->getFloat() != 0 || - config->option("seam_notch_outer")->getFloat() != 0); + bool have_notch = have_perimeters && (config->option("seam_notch_all")->get_float() != 0 || + config->option("seam_notch_inner")->get_float() != 0 || + config->option("seam_notch_outer")->get_float() != 0); toggle_field("seam_notch_angle", have_notch); bool have_gap_fill = !have_arachne; @@ -423,16 +423,18 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field("infill_first", (has_solid_infill || have_infill)); - for (auto el : { "fill_angle", "fill_angle_increment", "bridge_angle", "infill_extrusion_width", "infill_extrusion_spacing", "infill_extrusion_change_odd_layers", - "infill_speed" }) + for (auto el : {"fill_angle_cross","fill_angle_increment", "fill_angle_template", "fill_angle_cross", "bridge_angle", "infill_extrusion_width", + "infill_extrusion_spacing", "infill_extrusion_change_odd_layers", "infill_speed" }) toggle_field(el, have_infill || has_solid_infill); + + toggle_field("fill_angle", (have_infill || has_solid_infill) && ((ConfigOptionVectorBase*)config->option("fill_angle_template"))->size() != 0); toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill); toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill); //speed for (auto el : { "small_perimeter_min_length", "small_perimeter_max_length" }) - toggle_field(el, config->option("small_perimeter_speed")->getFloat() > 0); + toggle_field(el, config->option("small_perimeter_speed")->get_float() > 0); bool has_ironing_pattern = config->opt_enum("top_fill_pattern") == InfillPattern::ipSmooth || config->opt_enum("bottom_fill_pattern") == InfillPattern::ipSmooth @@ -509,14 +511,14 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) toggle_field(el, have_raft); //for default_extrusion_width/spacing, you need to ahve at least an extrusion_width with 0 - bool have_default_width = config->option("first_layer_extrusion_width")->getFloat() == 0 || - (config->option("perimeter_extrusion_width")->getFloat() == 0 && (have_perimeters || have_brim)) || - (config->option("external_perimeter_extrusion_width")->getFloat() == 0 && have_perimeters) || - (config->option("infill_extrusion_width")->getFloat() == 0 && (have_infill || has_solid_infill)) || - (config->option("solid_infill_extrusion_width")->getFloat() == 0 && has_solid_infill) || - (config->option("top_infill_extrusion_width")->getFloat() == 0 && has_top_solid_infill) || - (config->option("support_material_extrusion_width")->getFloat() == 0 && have_support_material) || - (config->option("skirt_extrusion_width")->getFloat() == 0 && have_skirt); + bool have_default_width = config->option("first_layer_extrusion_width")->get_float() == 0 || + (config->option("perimeter_extrusion_width")->get_float() == 0 && (have_perimeters || have_brim)) || + (config->option("external_perimeter_extrusion_width")->get_float() == 0 && have_perimeters) || + (config->option("infill_extrusion_width")->get_float() == 0 && (have_infill || has_solid_infill)) || + (config->option("solid_infill_extrusion_width")->get_float() == 0 && has_solid_infill) || + (config->option("top_infill_extrusion_width")->get_float() == 0 && has_top_solid_infill) || + (config->option("support_material_extrusion_width")->get_float() == 0 && have_support_material) || + (config->option("skirt_extrusion_width")->get_float() == 0 && have_skirt); toggle_field("extrusion_width", have_default_width); toggle_field("extrusion_spacing", have_default_width); diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 5c718f8021f..8bc942a6180 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -1558,10 +1558,10 @@ void PageDiameters::apply_custom_config(DynamicPrintConfig &config) config.option("infill_extrusion_width")->set_phony(true); config.option("solid_infill_extrusion_width")->set_phony(true); config.option("top_infill_extrusion_width")->set_phony(true); - config.set_key_value("perimeter_extrusion_change_odd_layers", new ConfigOptionFloat(0)); - config.set_key_value("external_perimeter_extrusion_change_odd_layers", new ConfigOptionFloat(0)); - config.set_key_value("infill_extrusion_change_odd_layers", new ConfigOptionFloat(0)); - config.set_key_value("solid_infill_extrusion_change_odd_layers", new ConfigOptionFloat(0)); + config.set_key_value("perimeter_extrusion_change_odd_layers", new ConfigOptionFloatOrPercent(0, false)); + config.set_key_value("external_perimeter_extrusion_change_odd_layers", new ConfigOptionFloatOrPercent(0, false)); + config.set_key_value("infill_extrusion_change_odd_layers", new ConfigOptionFloatOrPercent(0, false)); + config.set_key_value("solid_infill_extrusion_change_odd_layers", new ConfigOptionFloatOrPercent(0, false)); config.update_phony({}); } diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index ecd9cc819f9..e8af859eeb3 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -76,6 +76,35 @@ wxString get_points_string(const std::vector& values) return ret_str; } +std::pair get_strings_points(const wxString &str, double min, double max, std::vector &out_values) +{ + bool invalid_val = false; + bool out_of_range_val = false; + wxStringTokenizer points(str, ","); + while (points.HasMoreTokens()) { + wxString token = points.GetNextToken(); + double x, y; + wxStringTokenizer point(token, "x"); + if (point.HasMoreTokens()) { + wxString x_str = point.GetNextToken(); + if (x_str.ToDouble(&x) && point.HasMoreTokens()) { + wxString y_str = point.GetNextToken(); + if (y_str.ToDouble(&y) && !point.HasMoreTokens()) { + if (min <= x && x <= max && min <= y && y <= max) { + out_values.push_back(Vec2d(x, y)); + continue; + } + out_of_range_val = true; + break; + } + } + } + invalid_val = true; + break; + } + return {invalid_val, out_of_range_val}; +} + Field::~Field() { @@ -110,6 +139,8 @@ void Field::PostInitialize() auto tag_pos = m_opt_id.find("#"); if (tag_pos != std::string::npos) m_opt_idx = stoi(m_opt_id.substr(tag_pos + 1, m_opt_id.size())); + else + m_opt_idx = -1; // no index, ie full vector in serialized form break; } default: @@ -326,122 +357,327 @@ void RichTooltipTimer::Notify() { } } -bool Field::is_matched(const std::string& string, const std::string& pattern) +bool Field::is_matched(const std::string &string, const std::string &pattern) { std::regex regex_pattern(pattern, std::regex_constants::icase); // use ::icase to make the matching case insensitive like /i in perl return std::regex_match(string, regex_pattern); } -static wxString na_value() { return _(L("N/A")); } +static wxString na_value() { return _L("N/A"); } + +// return the string to set, and bool if there is a nil value +std::pair any_to_wxstring(const boost::any &value, const ConfigOptionDef &opt, const int opt_idx) +{ + wxString text_value; + bool has_nil = false; + auto deserialize = [&text_value, &value, &opt, &has_nil](ConfigOptionVectorBase &&writer, bool check_nil = true) { + writer.set_any(value, -1); + text_value = writer.serialize(); + if (check_nil && opt.nullable) + has_nil = (text_value.Replace(NIL_STR_VALUE, na_value()) > 0); + //replace ',' by ';' + text_value.Replace(",", ";"); + if (!is_decimal_separator_point()) { + // adjust to locale: '.' -> ',' + //',' are the decimal separator, transform from '.' from serialization (which happens in C locale) + text_value.Replace(".", ","); + } + }; + // first, easy convert-to one-string + switch (opt.type) { + case coFloats: + if (opt_idx < 0) { + deserialize(ConfigOptionFloats{}); + break; + } + case coPercents: + if (opt_idx < 0) { + deserialize(ConfigOptionPercents{}); + break; + } + if (opt.nullable && ConfigOptionFloatsNullable::is_nil(value)) { + text_value = na_value(); + has_nil = true; + break; + } + case coFloat: + case coPercent: text_value = double_to_string(boost::any_cast(value), opt.precision); break; + case coStrings: + if (opt_idx < 0) { + //custom for strings, as we don't need the serialized form, the normal one with ';' in-between is enough + ConfigOptionStrings reader; + reader.set_any(value, opt_idx); + std::string good_str; + for (std::string s : reader.values) good_str += s + ";"; + if(!good_str.empty()) + good_str.pop_back(); + text_value = good_str; + break; + } + // can't be nullable + case coString: text_value = boost::any_cast(value); break; + case coFloatsOrPercents: + if (opt_idx < 0) { + deserialize(ConfigOptionFloatsOrPercents{}); + break; + } + if (opt.nullable && + boost::any_cast(value) == ConfigOptionFloatsOrPercentsNullable::NIL_VALUE()) { + text_value = na_value(); + has_nil = true; + break; + } + case coFloatOrPercent: + FloatOrPercent fl_or_per = boost::any_cast(value); + text_value = double_to_string(fl_or_per.value); + if (fl_or_per.percent) + text_value.append("%"); + break; + case coBools: + if (opt_idx < 0) { + deserialize(ConfigOptionBools{}); + } else { + if (opt.nullable && boost::any_cast(value) == ConfigOptionBoolsNullable::NIL_VALUE()) { + text_value = na_value(); + has_nil = true; + break; + } + text_value = boost::any_cast(value) != 0 ? "true" : "false"; + } + break; + case coBool: + if (opt.is_script) + text_value = boost::any_cast(value) != 0 ? "true" : "false"; + else + text_value = boost::any_cast(value) ? "true" : "false"; + case coInts: + if (opt_idx < 0) { + deserialize(ConfigOptionInts{}); + break; + } + if (opt.nullable && boost::any_cast(value) == ConfigOptionIntsNullable::NIL_VALUE()) { + text_value = na_value(); + has_nil = true; + break; + } + case coInt: text_value = wxString::Format(_T("%i"), int(boost::any_cast(value))); break; + case coPoints: + if (opt_idx < 0) { + deserialize(ConfigOptionPoints{}); + assert(text_value == get_points_string(boost::any_cast>(value))); + break; + } + case coPoint: text_value = get_points_string({boost::any_cast(value)}); + } + return {text_value, has_nil}; +} + +// return true if the field isn't the same as before +bool TextField::get_vector_value(const wxString &str, ConfigOptionVectorBase &reader) +{ + std::string vector_str = str.ToStdString(); + if (str.size() > 2 && str.at(0) == '[' && str.at(str.size() - 1) == ']') { + // validate data inside + // first, remove all spaces + vector_str = str.SubString(1, str.size() - 1).ToStdString(); + } + // FIXME: also remove other unwanted chars only "[0-9].-,;" should remain + boost::erase_all(vector_str, " "); + bool is_decimal_sep_point = is_decimal_separator_point(); + if (!is_decimal_sep_point) { + //',' are the decimal separator, transform to '.' for deserialization (which happens in C locale) + boost::replace_all(vector_str, ",", "."); + } + boost::replace_all(vector_str, ";", ","); + try { + reader.deserialize(vector_str); + } catch (std::exception) {} + std::string good_str = reader.serialize(); + // replace ',' by ';' + boost::replace_all(good_str, ",", ";"); + if (!is_decimal_sep_point) { + // adjust to locale: '.' -> ',' + //',' are the decimal separator, transform from '.' from serialization (which happens in C locale) + boost::replace_all(good_str, ".", ","); + } + return (str.ToStdString() != good_str); +} -//TODO move value verification on another methos that won't be called at each value.get() -void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true*/) +//TODO move value verification on another methods that won't be called at each value.get() +void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = true*/) { + bool need_update = false; + // convert nil values to serializable ones + if (m_opt.nullable && (m_opt.type != coString && m_opt.type != coStrings)) { + need_update = str.Replace(na_value(), NIL_STR_VALUE); + } + + // val is needed at the end of this function, for "max_volumetric_speed" || "gap_fill_speed" (bad practice) double val = 0; - switch (m_opt.type) { - case coInt: - m_value = wxAtoi(str); - val = wxAtoi(str); - break; - case coPercent: - case coPercents: - case coFloats: - case coFloat:{ - if (m_opt.type == coPercent && !str.IsEmpty() && str.Last() == '%') - str.RemoveLast(); - else if (!str.IsEmpty() && str.Last() == '%') - { + switch (m_opt.type) { + case coInts: // not used yet + if (m_opt_idx < 0) { + ConfigOptionInts reader; + need_update = get_vector_value(str, reader); + m_value = reader.values; + break; + } // else: one int on m_opt_idx, done below + case coInt: { + m_value = wxAtoi(str); + val = wxAtoi(str); + break; + } + case coBools: // not used + if (m_opt_idx < 0) { + ConfigOptionBools reader; + need_update = get_vector_value(str, reader); + m_value = reader.values; + break; + } // else: one bool on m_opt_idx, done below + case coBool: { + wxString lower = str; + lower.LowerCase(); + if (m_opt.is_script || m_opt.type == coBools) { + m_value = (lower == "true" || lower == "1") ? uint8_t(1) : uint8_t(0); + } else { + m_value = lower == "true" || lower == "1"; + } + break; + } + case coPercents: //% are optional & copercents uses cofloats deserialize anyway + case coFloats: + if (m_opt_idx < 0) { + ConfigOptionFloats reader; + need_update = get_vector_value(str, reader); + m_value = reader.values; + break; + } + case coPercent: + case coFloat: { + if (m_opt.type == coPercent && !str.IsEmpty() && str.Last() == '%') + str.RemoveLast(); + else if (!str.IsEmpty() && str.Last() == '%') { if (!check_value) { m_value.clear(); break; } - wxString label = m_opt.full_label.empty() ? _(m_opt.label) : _(m_opt.full_label); + wxString label = m_opt.full_label.empty() ? _(m_opt.label) : _(m_opt.full_label); show_error(m_parent, from_u8((boost::format(_utf8(L("%s doesn't support percentage"))) % label).str())); - set_value(double_to_string(m_opt.min, m_opt.precision), true); - m_value = double(m_opt.min); - break; - } + set_text_value(double_to_string(m_opt.min, m_opt.precision).ToStdString(), true); + m_value = double(m_opt.min); + break; + } - bool is_na_value = m_opt.nullable && str == na_value(); + bool is_na_value = m_opt.nullable && str == NIL_STR_VALUE; - const char dec_sep = is_decimal_separator_point() ? '.' : ','; + const char dec_sep = is_decimal_separator_point() ? '.' : ','; const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; - // Replace the first incorrect separator in decimal number, + // Replace the first incorrect separator in decimal number, // if this value doesn't "N/A" value in some language // see https://github.com/prusa3d/PrusaSlicer/issues/6921 if (!is_na_value && str.Replace(dec_sep_alt, dec_sep, false) != 0) - set_value(str, false); + set_text_value(str.ToStdString(), false); if (str == dec_sep) val = 0.0; - else - { - if (is_na_value) - val = ConfigOptionFloatsNullable::nil_value(); - else if (!str.ToDouble(&val)) - { + else { + if (is_na_value) { + val = NAN; + m_value = ConfigOptionFloatsNullable::create_nil(); + break; + } else if (!str.ToDouble(&val)) { if (!check_value) { m_value.clear(); break; } + val = m_opt.min == INT_MIN ? std::max(0., m_opt.max) : m_opt.min; show_error(m_parent, _(L("Invalid numeric input."))); - set_value(double_to_string(val, m_opt.precision), true); + set_text_value(double_to_string(val, m_opt.precision).ToStdString(), true); } - if (m_opt.min > val || val > m_opt.max) - { + if (m_opt.min > val || val > m_opt.max) { if (!check_value) { m_value.clear(); break; } if (m_opt_id == "extrusion_multiplier") { if (m_value.empty() || boost::any_cast(m_value) != val) { - wxString msg_text = format_wxstr(_L("Input value is out of range\n" - "Are you sure that %s is a correct value and that you want to continue?"), str); -// wxMessageDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxICON_WARNING | wxYES | wxNO); - WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxYES | wxNO); + wxString msg_text = + format_wxstr(_L("Input value is out of range\n" + "Are you sure that %s is a correct value and that you want to continue?"), + str); + // wxMessageDialog dialog(m_parent, msg_text, _L("Parameter + // validation") + ": " + m_opt_id, wxICON_WARNING | wxYES | wxNO); + WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, + wxYES | wxNO); if (dialog.ShowModal() == wxID_NO) { if (m_value.empty()) { - if (m_opt.min > val) val = m_opt.min; - if (val > m_opt.max) val = m_opt.max; - } - else + if (m_opt.min > val) + val = m_opt.min; + if (val > m_opt.max) + val = m_opt.max; + } else val = boost::any_cast(m_value); - set_value(double_to_string(val, m_opt.precision), true); + set_text_value(double_to_string(val, m_opt.precision).ToStdString(), true); } } - } - else { + } else { show_error(m_parent, _L("Input value is out of range")); - if (m_opt.min > val) val = m_opt.min; - if (val > m_opt.max) val = m_opt.max; - set_value(double_to_string(val, m_opt.precision), true); + if (m_opt.min > val) + val = m_opt.min; + if (val > m_opt.max) + val = m_opt.max; + set_text_value(double_to_string(val, m_opt.precision).ToStdString(), true); } } } m_value = val; - break; } - case coString: - case coStrings: - m_value = std::string(str.ToUTF8().data()); break; + } + case coStrings: + if (m_opt_idx < 0) { + ConfigOptionStrings reader; + //don't remove spaces and things like that + try { + reader.deserialize(str.ToStdString()); + } catch (std::exception) {} + std::string good_str; + for (std::string s : reader.values) good_str += s + ";"; + if (!good_str.empty()) + good_str.pop_back(); + need_update = (str.ToStdString() != good_str); + m_value = reader.values; + break; + } + case coString: m_value = std::string(str.ToUTF8().data()); break; case coFloatsOrPercents: + if (m_opt_idx < 0) { // not used yet + ConfigOptionFloatsOrPercents reader; + need_update = get_vector_value(str, reader); + m_value = reader.values; + break; + } case coFloatOrPercent: { + bool is_percent = false; if (!str.IsEmpty()) { if ("infill_overlap" == m_opt_id && m_last_validated_value != str) { bool bad = false; if (str.Last() != '%') { + is_percent = false; if (str.ToDouble(&val)) { - const DynamicPrintConfig& printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config; - const std::vector& nozzle_diameters = printer_config.option("nozzle_diameter")->values; - double nozzle_diameter = 0; - for (double diameter : nozzle_diameters) - nozzle_diameter = std::max(nozzle_diameter, diameter); + const DynamicPrintConfig &printer_config = + wxGetApp().preset_bundle->printers.get_edited_preset().config; + const std::vector &nozzle_diameters = + printer_config.option("nozzle_diameter")->values; + double nozzle_diameter = 0; + for (double diameter : nozzle_diameters) + nozzle_diameter = std::max(nozzle_diameter, diameter); if (val > nozzle_diameter / 2) { bad = true; } } } else { + is_percent = true; if (str.substr(0, str.size() - 1).ToCDouble(&val)) { if (val >= 50) { bad = true; @@ -449,65 +685,76 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true } } if (bad && check_value) { - const wxString msg_text = from_u8(_u8L("The infill / perimeter encroachment can't be higher than half of the perimeter width.\n" - "Are you sure to use this value?")); - wxMessageDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxICON_WARNING | wxYES | wxNO); - auto ret = dialog.ShowModal(); + const wxString msg_text = from_u8( + _u8L("The infill / perimeter encroachment can't be higher than half of the perimeter width.\n" + "Are you sure to use this value?")); + wxMessageDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, + wxICON_WARNING | wxYES | wxNO); + auto ret = dialog.ShowModal(); if (ret == wxID_NO) { - str = from_u8("49%"); + str = from_u8("49%"); m_last_validated_value = str; - set_value(str, false); + set_text_value(str.ToStdString(), false); str = m_last_validated_value; } m_last_validated_value = str; } - } - else if (str.Last() != '%') { - const char dec_sep = is_decimal_separator_point() ? '.' : ','; + } else if (str.Last() != '%') { + is_percent = false; + const char dec_sep = is_decimal_separator_point() ? '.' : ','; const char dec_sep_alt = dec_sep == '.' ? ',' : '.'; // Replace the first incorrect separator in decimal number. if (str.Replace(dec_sep_alt, dec_sep, false) != 0) - set_value(str, false); + set_text_value(str.ToStdString(), false); // remove space and "mm" substring, if any exists str.Replace(" ", "", true); str.Replace("m", "", true); - if (m_opt.nullable && str == na_value()) { - val = ConfigOptionFloatsNullable::nil_value(); - str = "nan"; + if (m_opt.nullable && str == NIL_STR_VALUE) { + m_value = ConfigOptionFloatsOrPercentsNullable::NIL_VALUE(); + break; } else if (!str.ToDouble(&val)) { if (!check_value) { m_value.clear(); break; } show_error(m_parent, _(L("Invalid numeric input."))); - set_value(double_to_string(val, m_opt.precision), true); + set_value(FloatOrPercent{val, is_percent}, true); } else { - - //at least check min, as we can want a 0 min - if (m_opt.min > val) - { + //convert m_value into str to compare + FloatOrPercent val_from_m_value = m_value.empty() ? FloatOrPercent{0, false} : + boost::any_cast(m_value); + wxString str_from_m_value = double_to_string(val_from_m_value.value, m_opt.precision); + if (val_from_m_value.percent) + str_from_m_value += '%'; + + // at least check min, as we can want a 0 min + if (m_opt.min > val) { if (!check_value) { m_value.clear(); break; } show_error(m_parent, _(L("Input value is out of range"))); - if (m_opt.min > val) val = m_opt.min; - set_value(double_to_string(val, m_opt.precision), true); - } else if (m_value.empty() || into_u8(str) != boost::any_cast(m_value)) { + if (m_opt.min > val) + val = m_opt.min; + set_value(FloatOrPercent{val, is_percent}, true); + } else if (m_value.empty() || str != str_from_m_value) { + // empty of not equal -> need check bool not_ok = (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max); if (!not_ok && m_opt.max_literal.value != 0 && val != 0) { if (m_opt.max_literal.percent) { - const DynamicPrintConfig& printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config; - const std::vector& nozzle_diameters = printer_config.option("nozzle_diameter")->values; + const DynamicPrintConfig &printer_config = + wxGetApp().preset_bundle->printers.get_edited_preset().config; + const std::vector &nozzle_diameters = + printer_config.option("nozzle_diameter")->values; double nozzle_diameter = 0; for (double diameter : nozzle_diameters) nozzle_diameter = std::max(nozzle_diameter, diameter); if (m_opt.max_literal.value > 0) not_ok = val > nozzle_diameter * m_opt.max_literal.value; else - not_ok = val < nozzle_diameter* (-m_opt.max_literal.value); + not_ok = val < nozzle_diameter * (-m_opt.max_literal.value); } else { if (m_opt.max_literal.value > 0) not_ok = val > m_opt.max_literal.value; @@ -521,102 +768,99 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true break; } - bool infill_anchors = m_opt.opt_key == "infill_anchor" || m_opt.opt_key == "infill_anchor_max"; - - const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm"; - const wxString stVal = double_to_string(val, m_opt.precision); - const wxString msg_text = from_u8((boost::format(_u8L("Do you mean %s%% instead of %s %s?\n" - "Select YES if you want to change this value to %s%%, \n" - "or NO if you are sure that %s %s is a correct value.")) % stVal % stVal % sidetext % stVal % stVal % sidetext).str()); - WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxYES | wxNO); + bool infill_anchors = m_opt.opt_key == "infill_anchor" || + m_opt.opt_key == "infill_anchor_max"; + + const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : + "mm"; + const wxString stVal = double_to_string(val, m_opt.precision); + const wxString msg_text = from_u8( + (boost::format(_u8L("Do you mean %s%% instead of %s %s?\n" + "Select YES if you want to change this value to %s%%, \n" + "or NO if you are sure that %s %s is a correct value.")) % + stVal % stVal % sidetext % stVal % stVal % sidetext) + .str()); + WarningDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, + wxYES | wxNO); if ((!infill_anchors || val > 100) && dialog.ShowModal() == wxID_YES) { str += "%"; + is_percent = true; m_last_validated_value = str; - set_value(str, false/*true*/); + set_value(FloatOrPercent{val, is_percent}, false /*true*/); str = m_last_validated_value; } else - set_value(stVal, false); // it's no needed but can be helpful, when inputted value contained "," instead of "." + set_value(FloatOrPercent{val, is_percent}, false); // it's no needed but can be helpful, when inputted value + // contained "," instead of "." m_last_validated_value = str; } } } } else { str.ToDouble(&val); + is_percent = true; } } - m_value = into_u8(str); + m_value = FloatOrPercent{val, is_percent}; break; } case coPoints: { std::vector out_values; str.Replace(" ", wxEmptyString, true); if (!str.IsEmpty()) { - bool invalid_val = false; - bool out_of_range_val = false; - wxStringTokenizer points(str, ","); - while (points.HasMoreTokens()) { - wxString token = points.GetNextToken(); - double x, y; - wxStringTokenizer point(token, "x"); - if (point.HasMoreTokens()) { - wxString x_str = point.GetNextToken(); - if (x_str.ToDouble(&x) && point.HasMoreTokens()) { - wxString y_str = point.GetNextToken(); - if (y_str.ToDouble(&y) && !point.HasMoreTokens()) { - if (m_opt.min <= x && x <= m_opt.max && m_opt.min <= y && y <= m_opt.max) { - out_values.push_back(Vec2d(x, y)); - continue; - } - out_of_range_val = true; - break; - } - } - } - invalid_val = true; - break; - } + auto [/*bool*/ invalid_val, /*bool*/ out_of_range_val] = get_strings_points(str, m_opt.min, m_opt.max, + out_values); if (out_of_range_val) { wxString text_value; if (!m_value.empty()) text_value = get_points_string(boost::any_cast>(m_value)); - set_value(text_value, true); + set_text_value(text_value.ToStdString(), true); show_error(m_parent, _L("Input value is out of range")); - } - else if (invalid_val) { + } else if (invalid_val) { wxString text_value; if (!m_value.empty()) text_value = get_points_string(boost::any_cast>(m_value)); - set_value(text_value, true); - show_error(m_parent, format_wxstr(_L("Invalid input format. Expected vector of dimensions in the following format: \"%1%\""),"XxY, XxY, ..." )); + set_text_value(text_value.ToStdString(), true); + show_error(m_parent, format_wxstr(_L("Invalid input format. Expected vector of dimensions in the " + "following format: \"%1%\""), + "XxY, XxY, ...")); } } m_value = out_values; - break; } + break; + } - default: - break; - } + default: break; + } if (!Field::warn_zero_gapfillspeed && ("max_volumetric_speed" == m_opt_id || "gap_fill_speed" == m_opt_id)) { - bool show_warning = false; - const DynamicPrintConfig& print_config = wxGetApp().preset_bundle->fff_prints.get_edited_preset().config; + bool show_warning = false; + const DynamicPrintConfig &print_config = wxGetApp().preset_bundle->fff_prints.get_edited_preset().config; if ("max_volumetric_speed" == m_opt_id && val > 0) show_warning = print_config.option("gap_fill_speed")->value == 0; if ("gap_fill_speed" == m_opt_id && val == 0) show_warning = true; if (show_warning) { - const wxString msg_text = from_u8(_u8L("Auto Speed will try to maintain a constant flow rate accross all print moves." - "\nIt is not recommended to include gap moves to the Auto Speed calculation(by setting this value to 0)." - "\nVery thin gap extrusions will often not max out the flow rate of your printer." - "\nAs a result, this will cause Auto Speed to lower the speeds of all other print moves to match the low flow rate of these thin gaps.")); - wxMessageDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, wxICON_WARNING | wxOK); + const wxString msg_text = from_u8( + _u8L("Auto Speed will try to maintain a constant flow rate accross all print moves." + "\nIt is not recommended to include gap moves to the Auto Speed calculation(by setting this " + "value to 0)." + "\nVery thin gap extrusions will often not max out the flow rate of your printer." + "\nAs a result, this will cause Auto Speed to lower the speeds of all other print moves to " + "match the low flow rate of these thin gaps.")); + wxMessageDialog dialog(m_parent, msg_text, _L("Parameter validation") + ": " + m_opt_id, + wxICON_WARNING | wxOK); dialog.ShowModal(); Field::warn_zero_gapfillspeed = true; } } + + if (need_update) { + wxString new_str = any_to_wxstring(m_value, m_opt, m_opt_idx).first; + set_text_value(new_str.ToStdString()); + } } void Field::msw_rescale() @@ -648,60 +892,16 @@ void TextCtrl::BUILD() { wxString text_value = wxString(""); - switch (m_opt.type) { - case coFloatOrPercent: - { - text_value = double_to_string(m_opt.default_value->getFloat(), m_opt.precision); - if (m_opt.get_default_value()->percent) - text_value += "%"; - break; - } - case coPercent: - { - text_value = double_to_string(m_opt.default_value->getFloat(), m_opt.precision); - text_value += "%"; - break; - } - case coPercents: - case coFloats: - case coFloat: - { - double val = m_opt.type == coFloats ? - m_opt.get_default_value()->get_at(m_opt_idx) : - m_opt.type == coFloat ? - m_opt.default_value->getFloat() : - m_opt.get_default_value()->get_at(m_opt_idx); - text_value = double_to_string(val, m_opt.precision); - break; - } - case coFloatsOrPercents: - { - const ConfigOptionFloatsOrPercents* cofop = m_opt.get_default_value(); - text_value = double_to_string(cofop->get_at(m_opt_idx).value, m_opt.precision); - if (cofop->get_at(m_opt_idx).percent) - text_value += "%"; - break; + boost::any anyval = m_opt.default_value->get_any(m_opt_idx); + text_value = any_to_wxstring(m_opt.default_value->get_any(m_opt_idx), m_opt, m_opt_idx).first; + if (text_value == na_value()) { + // current value is nil, get the not-nil default value of the default option. + assert(m_opt.default_value->is_vector()); + m_last_meaningful_value = any_to_wxstring(static_cast(m_opt.default_value.get())->get_default_value(), m_opt, m_opt_idx).first; + } else { + m_last_meaningful_value = text_value; } - case coString: - text_value = m_opt.get_default_value()->value; - break; - case coStrings: - { - const ConfigOptionStrings *vec = m_opt.get_default_value(); - if (vec == nullptr || vec->empty()) break; //for the case of empty default value - text_value = vec->get_at(m_opt_idx); - break; - } - case coPoint: - text_value = get_points_string({ m_opt.get_default_value()->value }); - break; - case coPoints: - text_value = get_points_string(m_opt.get_default_value()->values); - break; - default: - break; - } - m_last_meaningful_value = text_value; + assert(m_last_meaningful_value != na_value()); long style = m_opt.multiline ? wxTE_MULTILINE : wxTE_PROCESS_ENTER; #ifdef _WIN32 @@ -770,7 +970,15 @@ void TextCtrl::BUILD() { window = dynamic_cast(temp); this->set_tooltip(text_value); -} +} + + +void TextCtrl::set_text_value(const std::string &value, bool change_event) +{ + m_disable_change_event = !change_event; + dynamic_cast(window)->SetValue(value); + m_disable_change_event = false; +} bool TextCtrl::value_was_changed() { @@ -784,22 +992,78 @@ bool TextCtrl::value_was_changed() get_value_by_opt_type(ret_str); switch (m_opt.type) { + case coInts: + if (m_opt_idx < 0) { + return boost::any_cast>(m_value) != boost::any_cast>(val); + } + if (m_opt.nullable) { + uint8_t new_val = boost::any_cast(m_value); + uint8_t old_val = boost::any_cast(val); + if (new_val == ConfigOptionInts::NIL_VALUE() && old_val == ConfigOptionInts::NIL_VALUE()) + return false; + } case coInt: return boost::any_cast(m_value) != boost::any_cast(val); - case coPercent: case coPercents: case coFloats: + if (m_opt_idx < 0) { + return boost::any_cast>(m_value) != boost::any_cast>(val); + } + if (m_opt.nullable) { + double new_val = boost::any_cast(m_value); + double old_val = boost::any_cast(val); + if ((std::isnan(new_val) || ConfigOptionFloats::is_nil(m_value)) && + (std::isnan(old_val) || ConfigOptionFloats::is_nil(val))) + return false; + } + case coPercent: case coFloat: { - if (m_opt.nullable && std::isnan(boost::any_cast(m_value)) && - std::isnan(boost::any_cast(val))) - return false; return boost::any_cast(m_value) != boost::any_cast(val); } - case coString: case coStrings: - case coFloatOrPercent: - case coFloatsOrPercents: + if (m_opt_idx < 0) { + return boost::any_cast>(m_value) != + boost::any_cast>(val); + } + case coString: return boost::any_cast(m_value) != boost::any_cast(val); + case coFloatsOrPercents: + if (m_opt_idx < 0) { + return boost::any_cast>(m_value) != + boost::any_cast>(val); + } + if (m_opt.nullable) { + FloatOrPercent new_val = boost::any_cast(m_value); + FloatOrPercent old_val = boost::any_cast(val); + if ((std::isnan(new_val.value) || new_val == ConfigOptionFloatsOrPercents::NIL_VALUE()) && + (std::isnan(old_val.value) || old_val == ConfigOptionFloatsOrPercents::NIL_VALUE())) + return false; + } + case coFloatOrPercent: + return boost::any_cast(m_value) != boost::any_cast(val); + case coPoints: + if (m_opt_idx < 0) { + return boost::any_cast>(m_value) != boost::any_cast>(val); + } + case coPoint: + return boost::any_cast(m_value) != boost::any_cast(val); + case coBools: + if (m_opt_idx < 0) { + return boost::any_cast>(m_value) != boost::any_cast>(val); + } else { + if (m_opt.nullable) { + uint8_t new_val = boost::any_cast(m_value); + uint8_t old_val = boost::any_cast(val); + if (new_val == ConfigOptionBools::NIL_VALUE() && old_val == ConfigOptionBools::NIL_VALUE()) + return false; + } + return boost::any_cast(m_value) != boost::any_cast(val); + } + case coBool: + if(m_opt.is_script) + return boost::any_cast(m_value) != boost::any_cast(val); + else + return boost::any_cast(m_value) != boost::any_cast(val); default: return true; } @@ -813,18 +1077,29 @@ void TextCtrl::propagate_value() on_kill_focus(); else if (value_was_changed()) on_change_field(); + //update m_last_meaningful_value ? + if (!m_value.empty() && dynamic_cast(window)->GetValue() != na_value()) + m_last_meaningful_value = dynamic_cast(window)->GetValue(); } void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/) { + //can be: + //case coFloat: + //case coFloats: + //case coPercent: + //case coPercents: + //case coFloatOrPercent: + //case coFloatsOrPercents: + //case coString: + //case coStrings: + // coBools (if all) + // coInts (if all) + // coPoints (if all) + auto [/*wxString*/text_value, /*bool*/ has_nil] = any_to_wxstring(value, m_opt, m_opt_idx); + if (!has_nil) + m_last_meaningful_value = text_value; m_disable_change_event = !change_event; - if (m_opt.nullable) { - const bool m_is_na_val = boost::any_cast(value) == na_value(); - if (!m_is_na_val) - m_last_meaningful_value = value; - dynamic_cast(window)->SetValue(m_is_na_val ? na_value() : boost::any_cast(value)); - } - else - dynamic_cast(window)->SetValue(boost::any_cast(value)); + dynamic_cast(window)->SetValue(text_value); m_disable_change_event = false; if (!change_event) { @@ -839,7 +1114,7 @@ void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/) void TextCtrl::set_last_meaningful_value() { - dynamic_cast(window)->SetValue(boost::any_cast(m_last_meaningful_value)); + dynamic_cast(window)->SetValue(m_last_meaningful_value); propagate_value(); } @@ -897,12 +1172,10 @@ void CheckBox::BUILD() { if (m_opt.height >= 0) size.SetHeight(m_opt.height * m_em_unit); if (m_opt.width >= 0) size.SetWidth(m_opt.width * m_em_unit); - bool check_value = m_opt.type == coBool ? - m_opt.default_value->getBool() : m_opt.type == coBools ? - m_opt.get_default_value()->get_at(m_opt_idx) : - false; + bool check_value = m_opt.type == coBool || m_opt.type == coBools ? m_opt.default_value->get_bool(m_opt_idx) : + false; - m_last_meaningful_value = static_cast(check_value); + m_last_meaningful_value = static_cast(check_value); #ifdef __WXGTK2__ //gtk2 can't resize checkboxes, so we are using togglable buttons instead @@ -968,13 +1241,15 @@ void CheckBox::set_widget_value(bool new_val) #endif } -void CheckBox::set_value(const boost::any& value, bool change_event) +void CheckBox::set_value(const boost::any &value, bool change_event) { + //can be coBool and coBools (with idx) m_disable_change_event = !change_event; - if (m_opt.nullable) { - m_is_na_val = boost::any_cast(value) == ConfigOptionBoolsNullable::nil_value(); + assert(m_opt.type == coBool || (m_opt.type == coBools && m_opt_idx >= 0)); + if (m_opt.type == coBools && m_opt.nullable) { + m_is_na_val = boost::any_cast(value) == ConfigOptionBoolsNullable::NIL_VALUE(); if (!m_is_na_val) - m_last_meaningful_value = value; + m_last_meaningful_value = boost::any_cast(value); set_widget_value(m_is_na_val ? false : boost::any_cast(value) != 0); } else if (m_opt.is_script) { uint8_t val = boost::any_cast(value); @@ -982,8 +1257,12 @@ void CheckBox::set_value(const boost::any& value, bool change_event) dynamic_cast(window)->Set3StateValue(wxCheckBoxState::wxCHK_UNDETERMINED); else set_widget_value(val != 0); - } else + } else if (m_opt.type == coBools) { + set_widget_value(boost::any_cast(value) != 0); + } else { + assert(m_opt.type == coBool); set_widget_value(boost::any_cast(value)); + } m_disable_change_event = false; } @@ -991,7 +1270,7 @@ void CheckBox::set_last_meaningful_value() { if (m_opt.nullable) { m_is_na_val = false; - set_widget_value(boost::any_cast(m_last_meaningful_value) != 0); + set_widget_value(m_last_meaningful_value != 0); on_change_field(); } } @@ -1023,7 +1302,7 @@ boost::any& CheckBox::get_value() if (m_opt.type == coBool) m_value = static_cast(value); else - m_value = m_is_na_val ? ConfigOptionBoolsNullable::nil_value() : static_cast(value); + m_value = m_is_na_val ? ConfigOptionBoolsNullable::NIL_VALUE() : static_cast(value); return m_value; } @@ -1055,7 +1334,7 @@ void SpinCtrl::BUILD() { switch (m_opt.type) { case coInt: - default_value = m_opt.default_value->getInt(); + default_value = m_opt.default_value->get_int(); text_value = wxString::Format(_T("%i"), default_value); break; case coInts: @@ -1357,17 +1636,17 @@ void Choice::set_selection() choice_ctrl* field = dynamic_cast(window); switch (m_opt.type) { case coEnum:{ - field->SetSelection(m_opt.default_value->getInt()); + field->SetSelection(m_opt.default_value->get_int()); break; } case coFloat: case coPercent: { - double val = m_opt.default_value->getFloat(); + double val = m_opt.default_value->get_float(); text_value = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 1); break; } case coInt:{ - text_value = wxString::Format(_T("%i"), int(m_opt.default_value->getInt())); + text_value = wxString::Format(_T("%i"), int(m_opt.default_value->get_int())); break; } case coStrings:{ @@ -1375,7 +1654,7 @@ void Choice::set_selection() break; } case coFloatOrPercent: { - text_value = double_to_string(m_opt.default_value->getFloat(), m_opt.precision); + text_value = double_to_string(m_opt.default_value->get_float(), m_opt.precision); if (m_opt.get_default_value()->percent) text_value += "%"; break; @@ -1394,7 +1673,7 @@ void Choice::set_selection() } } -void Choice::set_value(const std::string& value, bool change_event) //! Redundant? +void Choice::set_text_value(const std::string &value, bool change_event) //! Redundant? { m_disable_change_event = !change_event; @@ -1439,8 +1718,16 @@ int32_t Choice::idx_from_enum_value(int32_t val) { return 0; } -void Choice::set_value(const boost::any& value, bool change_event) +void Choice::set_value(const boost::any &value, bool change_event) { + // can be + // GUIType::select_open + // GUIType::f_enum_open: + // GUIType::i_enum_open: + // coEnum + assert(m_opt.type == coEnum || m_opt.gui_type == ConfigOptionDef::GUIType::select_open || + m_opt.gui_type == ConfigOptionDef::GUIType::f_enum_open || + m_opt.gui_type == ConfigOptionDef::GUIType::i_enum_open); m_disable_change_event = !change_event; choice_ctrl* field = dynamic_cast(window); @@ -1451,12 +1738,9 @@ void Choice::set_value(const boost::any& value, bool change_event) case coPercent: case coFloatOrPercent: case coString: - case coStrings: { - wxString text_value; - if (m_opt.type == coInt) - text_value = wxString::Format(_T("%i"), int(boost::any_cast(value))); - else - text_value = boost::any_cast(value); + case coStrings: { + auto [/*wxString*/ text_value, /*bool*/ has_nil] = any_to_wxstring(value, m_opt, m_opt_idx); + size_t idx = 0; const std::vector& enums = m_opt.enum_values.empty() ? m_opt.enum_labels : m_opt.enum_values; for (auto el : enums) @@ -1474,6 +1758,7 @@ void Choice::set_value(const boost::any& value, bool change_event) else field->SetSelection(idx); + // merill note: i don't like hacks like that. makes the code spagetti if (!m_value.empty() && m_opt.opt_key == "fill_density") { // If m_value was changed before, then update m_value here too to avoid case // when control's value is already changed from the ConfigManipulation::update_print_fff_config(), @@ -1529,7 +1814,7 @@ void Choice::convert_to_enum_value(int32_t ret_enum) { m_value = value; } else - m_value = m_opt.default_value.get()->getInt(); + m_value = m_opt.default_value->get_int(); } //Please don't use that on Enum fields it will just break everything @@ -1704,10 +1989,11 @@ void ColourPicker::set_undef_value(wxColourPickerCtrl* field) btn->SetBitmapLabel(bmp); } -void ColourPicker::set_value(const boost::any& value, bool change_event) +void ColourPicker::set_value(const boost::any &value, bool change_event) { + // can be ConfigOptionDef::GUIType::color m_disable_change_event = !change_event; - const wxString clr_str(boost::any_cast(value)); + const wxString clr_str(boost::any_cast(value)); auto field = dynamic_cast(window); wxColour clr(clr_str); @@ -1767,10 +2053,10 @@ void PointCtrl::BUILD() const wxSize field_size(4 * m_em_unit, -1); Vec2d default_pt; - if(m_opt.type==coPoint) + if (m_opt.type == coPoint) default_pt = m_opt.get_default_value()->value; else // coPoints - default_pt = m_opt.get_default_value()->values.at(0); + default_pt = m_opt.get_default_value()->get_at(0); double val = default_pt(0); wxString X = val - int(val) == 0 ? wxString::Format(_T("%i"), int(val)) : wxNumberFormatter::ToString(val, 2, wxNumberFormatter::Style_None); val = default_pt(1); @@ -1866,7 +2152,7 @@ void PointCtrl::propagate_value(wxTextCtrl* win) on_change_field(); } -void PointCtrl::set_value(const Vec2d& value, bool change_event) +void PointCtrl::set_vec2d_value(const Vec2d& value, bool change_event) { m_disable_change_event = !change_event; @@ -1878,22 +2164,12 @@ void PointCtrl::set_value(const Vec2d& value, bool change_event) m_disable_change_event = false; } -void PointCtrl::set_value(const boost::any& value, bool change_event) +void PointCtrl::set_value(const boost::any &value, bool change_event) { - Vec2d pt(Vec2d::Zero()); - const Vec2d *ptf = boost::any_cast(&value); - if (!ptf) - { - if (m_opt.type == coPoint) { - pt = boost::any_cast(value)->value; - } else { // coPoints - ConfigOptionPoints* pts = boost::any_cast(value); - pt = pts->values.at(0); - } - } - else - pt = *ptf; - set_value(pt, change_event); + // can be coPoint and coPoints (with idx) + assert(m_opt.type == coPoint || (m_opt.type == coPoints && m_opt_idx >= 0)); + Vec2d pt = boost::any_cast(value); + set_vec2d_value(pt, change_event); } boost::any& PointCtrl::get_value() @@ -1902,7 +2178,7 @@ boost::any& PointCtrl::get_value() if (!x_textctrl->GetValue().ToDouble(&x) || !y_textctrl->GetValue().ToDouble(&y)) { - set_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true); + set_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true); show_error(m_parent, _L("Invalid numeric input.")); } else @@ -1913,7 +2189,7 @@ boost::any& PointCtrl::get_value() if (x > m_opt.max) x = m_opt.max; if (m_opt.min > y) y = m_opt.min; if (y > m_opt.max) y = m_opt.max; - set_value(Vec2d(x, y), true); + set_vec2d_value(Vec2d(x, y), true); show_error(m_parent, _L("Input value is out of range")); } @@ -2005,24 +2281,34 @@ void SliderCtrl::BUILD() m_sizer = dynamic_cast(temp); } -void SliderCtrl::set_value(const boost::any& value, bool change_event) +void SliderCtrl::set_value(const boost::any &value, bool change_event) { + // only with ConfigOptionDef::GUIType::slider: & coFloat or coInt + assert(m_opt.gui_type == ConfigOptionDef::GUIType::slider && (m_opt.type == coFloat || m_opt.type == coInt)); m_disable_change_event = !change_event; - - m_slider->SetValue(boost::any_cast(value)*m_scale); - int val = boost::any_cast(get_value()); - m_textctrl->SetLabel(wxString::Format("%d", val)); + if (m_opt.type == coFloat) { + m_slider->SetValue(boost::any_cast(value) * m_scale); + double val = boost::any_cast(get_value()); + m_textctrl->SetLabel(wxString::Format("%d", val)); + } else if (m_opt.type == coInt) { + m_slider->SetValue(boost::any_cast(value) * m_scale); + int32_t val = boost::any_cast(get_value()); + m_textctrl->SetLabel(wxString::Format("%d", val)); + } m_disable_change_event = false; } -boost::any& SliderCtrl::get_value() +boost::any &SliderCtrl::get_value() { -// int ret_val; -// x_textctrl->GetValue().ToDouble(&val); - return m_value = int(m_slider->GetValue()/m_scale); + // int ret_val; + // x_textctrl->GetValue().ToDouble(&val); + if (m_opt.type == coFloat) { + return m_value = double(m_slider->GetValue() / m_scale); + } else if (m_opt.type == coInt) { + return m_value = int32_t(m_slider->GetValue() / m_scale); + } } - } // GUI } // Slic3r diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index a567b5d67a5..574cda881e2 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -38,6 +38,8 @@ using t_back_to_init = std::function; wxString double_to_string(double const value, const int max_precision = 6); wxString get_points_string(const std::vector& values); +// return {invalid_val, out_of_range_val} +std::pair get_strings_points(const wxString &str, double min, double max, std::vector &out_values); class Field; class RichTooltipTimer : public wxTimer @@ -107,7 +109,7 @@ class Field { /// Copy of ConfigOption for deduction purposes const ConfigOptionDef m_opt {ConfigOptionDef()}; const t_config_option_key m_opt_id;//! {""}; - int m_opt_idx = 0; + int m_opt_idx = -1; // for saving state bool m_is_enable{true}; @@ -118,7 +120,7 @@ class Field { /// Sets a value for this control. /// subclasses should overload with a specific version /// Postcondition: Method does not fire the on_change event. - virtual void set_value(const boost::any& value, bool change_event) = 0; + virtual void set_value(const boost::any &value, bool change_event) = 0; virtual void set_last_meaningful_value() {} virtual void set_na_value() {} @@ -153,8 +155,7 @@ class Field { virtual wxSizer* getSizer() { return nullptr; } virtual wxWindow* getWindow() { return nullptr; } - bool is_matched(const std::string& string, const std::string& pattern); - void get_value_by_opt_type(wxString& str, const bool check_value = true); + bool is_matched(const std::string &string, const std::string &pattern); /// Factory method for generating new derived classes. template @@ -241,8 +242,6 @@ class Field { // current value boost::any m_value; - // last meaningful value - boost::any m_last_meaningful_value; // last validated value wxString m_last_validated_value; @@ -255,6 +254,23 @@ class Field { friend class OptionsGroup; }; +class TextField : public Field +{ + using Field::Field; +protected: + TextField(const ConfigOptionDef &opt, const t_config_option_key &id) : Field(opt, id) {} + TextField(wxWindow *parent, const ConfigOptionDef &opt, const t_config_option_key &id) : Field(parent, opt, id) + {} + ~TextField() {} + + void get_value_by_opt_type(wxString &str, const bool check_value = true); + bool get_vector_value(const wxString &str, ConfigOptionVectorBase &reader); + virtual void set_text_value(const std::string &str, bool change_event = false) = 0; + + // last meaningful value (can be whatever the child class want it to be) + wxString m_last_meaningful_value; +}; + /// Convenience function, accepts a const reference to t_field and checks to see whether /// or not both wx pointers are null. inline bool is_bad_field(const t_field& obj) { return obj->getSizer() == nullptr && obj->getWindow() == nullptr; } @@ -265,16 +281,16 @@ inline bool is_window_field(const t_field& obj) { return !is_bad_field(obj) && o /// Covenience function to determine whether this field is a valid sizer field. inline bool is_sizer_field(const t_field& obj) { return !is_bad_field(obj) && obj->getSizer() != nullptr; } -class TextCtrl : public Field { - using Field::Field; +class TextCtrl : public TextField { + using TextField::TextField; #ifdef __WXGTK__ bool bChangedValueEvent = true; void change_field_value(wxEvent& event); #endif //__WXGTK__ public: - TextCtrl(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} + TextCtrl(const ConfigOptionDef &opt, const t_config_option_key &id) : TextField(opt, id) {} + TextCtrl(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : TextField(parent, opt, id) {} ~TextCtrl() {} void BUILD() override; @@ -283,11 +299,7 @@ class TextCtrl : public Field { void propagate_value(); wxWindow* window {nullptr}; - void set_value(const std::string& value, bool change_event = false) { - m_disable_change_event = !change_event; - dynamic_cast(window)->SetValue(wxString(value)); - m_disable_change_event = false; - } + void set_text_value(const std::string &value, bool change_event = false) override; void set_value(const boost::any& value, bool change_event = false) override; void set_last_meaningful_value() override; void set_na_value() override; @@ -304,6 +316,8 @@ class TextCtrl : public Field { class CheckBox : public Field { using Field::Field; bool m_is_na_val {false}; + // last meaningful value (can be whatever the child class want it to be) + uint8_t m_last_meaningful_value; void set_widget_value(bool new_val); public: @@ -314,12 +328,12 @@ class CheckBox : public Field { wxWindow* window{ nullptr }; void BUILD() override; - void set_value(const bool value, bool change_event = false) { + void set_bool_value(const bool value, bool change_event = false) { m_disable_change_event = !change_event; dynamic_cast(window)->SetValue(value); m_disable_change_event = false; } - void set_value(const boost::any& value, bool change_event = false) override; + void set_value(const boost::any &value, bool change_event = false) override; void set_last_meaningful_value() override; void set_na_value() override; boost::any& get_value() override; @@ -348,7 +362,7 @@ class SpinCtrl : public Field { /// Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER void propagate_value() ; - void set_value(const std::string& value, bool change_event = false) { + void set_text_value(const std::string& value, bool change_event = false) { m_disable_change_event = !change_event; dynamic_cast(window)->SetValue(value); m_disable_change_event = false; @@ -373,16 +387,18 @@ class SpinCtrl : public Field { wxWindow* getWindow() override { return window; } }; -class Choice : public Field { - using Field::Field; +class Choice : public TextField +{ + using TextField::TextField; protected: //used by get_value when it's an enum //convert the value from the select to the enum value. store it in m_value void convert_to_enum_value(int32_t idx_val); int32_t idx_from_enum_value(int32_t enum_val); public: - Choice(const ConfigOptionDef& opt, const t_config_option_key& id) : Field(opt, id) {} - Choice(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id) : Field(parent, opt, id) {} + Choice(const ConfigOptionDef &opt, const t_config_option_key &id) : TextField(opt, id) {} + Choice(wxWindow *parent, const ConfigOptionDef &opt, const t_config_option_key &id) : TextField(parent, opt, id) + {} ~Choice() {} wxWindow* window{ nullptr }; @@ -399,8 +415,8 @@ class Choice : public Field { int m_last_selected { wxNOT_FOUND }; void set_selection(); - void set_value(const std::string& value, bool change_event = false); - void set_value(const boost::any& value, bool change_event = false) override; + void set_text_value(const std::string &value, bool change_event = false); + void set_value(const boost::any &value, bool change_event = false) override; void set_values(const std::vector &values); void set_values(const wxArrayString &values); boost::any& get_value() override; @@ -426,12 +442,12 @@ class ColourPicker : public Field { wxWindow* window{ nullptr }; void BUILD() override; - void set_value(const std::string& value, bool change_event = false) { + void set_text_value(const std::string& value, bool change_event = false) { m_disable_change_event = !change_event; dynamic_cast(window)->SetColour(value); m_disable_change_event = false; } - void set_value(const boost::any& value, bool change_event = false) override; + void set_value(const boost::any &value, bool change_event = false) override; boost::any& get_value() override; void msw_rescale() override; void sys_color_changed() override; @@ -456,8 +472,8 @@ class PointCtrl : public Field { bool value_was_changed(wxTextCtrl* win); // Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER void propagate_value(wxTextCtrl* win); - void set_value(const Vec2d& value, bool change_event = false); - void set_value(const boost::any& value, bool change_event = false) override; + void set_vec2d_value(const Vec2d& value, bool change_event = false); + void set_value(const boost::any &value, bool change_event = false) override; boost::any& get_value() override; void msw_rescale() override; @@ -484,7 +500,7 @@ class StaticText : public Field { wxWindow* window{ nullptr }; void BUILD() override; - void set_value(const std::string& value, bool change_event = false) { + void set_text_value(const std::string& value, bool change_event = false) { m_disable_change_event = !change_event; dynamic_cast(window)->SetLabel(wxString::FromUTF8(value.data())); m_disable_change_event = false; @@ -519,7 +535,7 @@ class SliderCtrl : public Field { void BUILD() override; - void set_value(const int value, bool change_event = false); + void set_int_value(const int value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false) override; boost::any& get_value() override; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2263616296f..2ba8dc8d53e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -279,8 +279,8 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const assert(extruders_min_height->values.size() == extruders_max_height->values.size()); assert(extruders_min_height->values.size() == nozzle_diameter->values.size()); for (size_t idx_extruder = 0; idx_extruder < extruders_min_height->values.size(); ++idx_extruder) { - min_height = std::min(min_height, float(extruders_min_height->get_abs_value(idx_extruder, nozzle_diameter->getFloat(idx_extruder)))); - max_height = std::max(max_height, float(extruders_max_height->get_abs_value(idx_extruder, nozzle_diameter->getFloat(idx_extruder)))); + min_height = std::min(min_height, float(extruders_min_height->get_abs_value(idx_extruder, nozzle_diameter->get_float(idx_extruder)))); + max_height = std::max(max_height, float(extruders_max_height->get_abs_value(idx_extruder, nozzle_diameter->get_float(idx_extruder)))); } min_height = check_z_step(min_height, z_step); max_height = check_z_step(max_height, z_step); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index ccd3cc08306..f785487ce54 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -99,139 +99,6 @@ const std::string& shortkey_alt_prefix() return str; } -// opt_index = 0, by the reason of zero-index in ConfigOptionVector by default (in case only one element) -void change_opt_value(DynamicConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) -{ - try{ - - if (config.def()->get(opt_key)->type == coBools && config.def()->get(opt_key)->nullable) { - ConfigOptionBoolsNullable* vec_new = new ConfigOptionBoolsNullable{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - return; - } - - const ConfigOptionDef *opt_def = config.def()->get(opt_key); - switch (opt_def->type) { - case coFloatOrPercent:{ - std::string str = boost::any_cast(value); - bool percent = false; - if (str.back() == '%') { - str.pop_back(); - percent = true; - } - double val = std::stod(str); // locale-dependent (on purpose - the input is the actual content of the field) - config.set_key_value(opt_key, new ConfigOptionFloatOrPercent(val, percent)); - break;} - case coFloatsOrPercents: { - std::string str = boost::any_cast(value); - bool percent = false; - if (str.back() == '%') { - str.pop_back(); - percent = true; - } - double val = stod(str); - ConfigOptionFloatsOrPercents* vec_new = new ConfigOptionFloatsOrPercents{ boost::any_cast(FloatOrPercent{val, percent}) }; - config.option(opt_key)->set_at(vec_new, opt_index, opt_index); - break; } - case coPercent: - config.set_key_value(opt_key, new ConfigOptionPercent(boost::any_cast(value))); - break; - case coFloat:{ - //config.set_key_value(opt_key, new ConfigOptionFloat(boost::any_cast(value))); - double& val_dbl = config.opt_float(opt_key); - val_dbl = boost::any_cast(value); - break; - } - case coPoint: { - config.set_key_value(opt_key, new ConfigOptionPoint(boost::any_cast(value))); - break; - } - case coPercents:{ - ConfigOptionPercents* vec_new = new ConfigOptionPercents{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, opt_index); - break; - } - case coFloats:{ - ConfigOptionFloats* vec_new = new ConfigOptionFloats{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, opt_index); - break; - } - case coString: { - //config.set_key_value(opt_key, new ConfigOptionString(boost::any_cast(value))); - std::string& val_str = config.opt_string(opt_key); - val_str = boost::any_cast(value); - break; - } - case coStrings:{ - if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "gcode_substitutions") { - config.option(opt_key)->values = - boost::any_cast>(value); - } - else if (config.def()->get(opt_key)->gui_flags.compare("serialized") == 0) { - std::string str = boost::any_cast(value); - std::vector values {}; - if (!str.empty()) { - if (str.back() == ';') str.pop_back(); - // Split a string to multiple strings by a semi - colon.This is the old way of storing multi - string values. - // Currently used for the post_process config value only. - boost::split(values, str, boost::is_any_of(";")); - if (values.size() == 1 && values[0] == "") - values.resize(0); - } - config.option(opt_key)->values = values; - } - else{ - ConfigOptionStrings* vec_new = new ConfigOptionStrings{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - } - } - break; - case coBool: - config.set_key_value(opt_key, new ConfigOptionBool(boost::any_cast(value))); - break; - case coBools:{ - ConfigOptionBools* vec_new = new ConfigOptionBools{ boost::any_cast(value) != 0 }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - break;} - case coInt:{ - //config.set_key_value(opt_key, new ConfigOptionInt(boost::any_cast(value))); - int& val_int = config.opt_int(opt_key); - val_int = boost::any_cast(value); - } - break; - case coInts:{ - ConfigOptionInts* vec_new = new ConfigOptionInts{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - } - break; - case coEnum:{ - ConfigOption* opt = opt_def->default_value.get()->clone(); - opt->setInt(boost::any_cast(value)); // we transport an int convertion of the enum in the boost anycast. - BOOST_LOG_TRIVIAL(debug) << "Set enum "<< opt_key << " as int " << boost::any_cast(value) << " into enum " << opt->serialize(); - config.set_key_value(opt_key, opt); - } - break; - case coPoints:{ - if (opt_key == "bed_shape") { - config.option(opt_key)->values = boost::any_cast>(value); - break; - } - ConfigOptionPoints* vec_new = new ConfigOptionPoints{ boost::any_cast(value) }; - config.option(opt_key)->set_at(vec_new, opt_index, 0); - } - break; - case coNone: - break; - default: - break; - } - } - catch (const std::exception &e) - { - wxLogError(format_wxstr("Internal error when changing value for %1%: %2%", opt_key, e.what())); - } -} - void show_error(wxWindow* parent, const wxString& message, bool monospaced_font) { ErrorDialog msg(parent, message, monospaced_font); @@ -293,7 +160,7 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio { const std::vector& labels = def->enum_labels; const std::vector& values = def->enum_values; - int val = conf_substitution.new_value->getInt(); + int val = conf_substitution.new_value->get_int(); bool is_infill = def->opt_key == "top_fill_pattern" || def->opt_key == "bottom_fill_pattern" || @@ -321,12 +188,12 @@ static void add_config_substitutions(const ConfigSubstitutions& conf_substitutio break; } case coBool: - new_val = conf_substitution.new_value->getBool() ? "true" : "false"; + new_val = conf_substitution.new_value->get_bool() ? "true" : "false"; break; case coBools: if (conf_substitution.new_value->nullable()) for (const char v : static_cast(conf_substitution.new_value.get())->values) - new_val += std::string(v == ConfigOptionBoolsNullable::nil_value() ? "nil" : v ? "true" : "false") + ", "; + new_val += std::string(v == ConfigOptionBoolsNullable::NIL_VALUE() ? "nil" : v ? "true" : "false") + ", "; else for (const char v : static_cast(conf_substitution.new_value.get())->values) new_val += std::string(v ? "true" : "false") + ", "; diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 21fb5c204a7..62ba9ab8a37 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -37,7 +37,7 @@ extern AppConfig* get_app_config(); extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); // Change option value in config -void change_opt_value(DynamicConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); +//void change_opt_value(DynamicConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); // If monospaced_font is true, the error message is displayed using html
tags, // so that the code formatting will be preserved. This is useful for reporting errors from the placeholder parser. diff --git a/src/slic3r/GUI/GUI_ObjectLayers.cpp b/src/slic3r/GUI/GUI_ObjectLayers.cpp index 55fba0e332c..99a66d335d7 100644 --- a/src/slic3r/GUI/GUI_ObjectLayers.cpp +++ b/src/slic3r/GUI/GUI_ObjectLayers.cpp @@ -131,7 +131,7 @@ wxSizer* ObjectLayers::create_layer(const t_layer_height_range& range, PlusMinus // Add control for the "Layer height" - editor = new LayerRangeEditor(this, double_to_string(m_object->layer_config_ranges[range].option("layer_height")->getFloat()), etLayerHeight, set_focus_data, + editor = new LayerRangeEditor(this, double_to_string(m_object->layer_config_ranges[range].option("layer_height")->get_float()), etLayerHeight, set_focus_data, [range](coordf_t layer_height, bool, bool) { return wxGetApp().obj_list()->edit_layer_range(range, layer_height); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index bcdf8d594e6..2bd179abeea 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -844,26 +844,26 @@ void Preview::update_layers_slider_mode() if (!objects.empty()) { const int extruder = objects[0]->config.has("extruder") ? - objects[0]->config.option("extruder")->getInt() : 0; + objects[0]->config.option("extruder")->get_int() : 0; auto is_one_extruder_printed_model = [objects, extruder]() { for (ModelObject* object : objects) { if (object->config.has("extruder") && - object->config.option("extruder")->getInt() != extruder) + object->config.option("extruder")->get_int() != extruder) return false; for (ModelVolume* volume : object->volumes) if ((volume->config.has("extruder") && - volume->config.option("extruder")->getInt() != 0 && // extruder isn't default - volume->config.option("extruder")->getInt() != extruder) || + volume->config.option("extruder")->get_int() != 0 && // extruder isn't default + volume->config.option("extruder")->get_int() != extruder) || !volume->mmu_segmentation_facets.empty()) return false; for (const auto& range : object->layer_config_ranges) if (range.second.has("extruder") && - range.second.option("extruder")->getInt() != extruder) + range.second.option("extruder")->get_int() != extruder) return false; } return true; diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 3181b99b2d0..e41c1271f7a 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -80,12 +80,12 @@ void ArrangeJob::clear_input() void add_brim(arrangement::ArrangePolygon &ap, const ModelConfigObject &config, const Plater* plater) { - if (plater->config()->option("brim_per_object")->getBool()) { + if (plater->config()->option("brim_per_object")->get_bool()) { // object-brim increase the size of the object // Should be using the "inflation" field but it's non-functional right now. - coord_t diff = scale_(plater->config()->option("brim_width")->getFloat() - plater->config()->option("extruder_clearance_radius")->getFloat() / 2); + coord_t diff = scale_(plater->config()->option("brim_width")->get_float() - plater->config()->option("extruder_clearance_radius")->get_float() / 2); if (config.option("brim_width")) - diff = scale_(config.option("brim_width")->getFloat() - plater->config()->option("extruder_clearance_radius")->getFloat() / 2); + diff = scale_(config.option("brim_width")->get_float() - plater->config()->option("extruder_clearance_radius")->get_float() / 2); if (diff > 0) { ExPolygons brimmed = offset_ex(ap.poly, diff); assert(brimmed.size() == 1); diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 553e741765d..c4fb6e0988b 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -582,7 +582,9 @@ void OG_CustomCtrl::CtrlLine::update_visibility(ConfigOptionMode mode) { if (og_line.is_separator()) return; - const std::vector