From de70415af12e9577fb17b0c23b6f0700d6ee63a3 Mon Sep 17 00:00:00 2001 From: supermerill Date: Tue, 19 Dec 2023 01:35:17 +0100 Subject: [PATCH] Simplify the field <-> config pipes allow all vector types in text field (';' separated, to avoid problem with ',' vs '.' locales) Change `post_process` to use the new way (no "serialized" needed anymore) Remove "one_string" gui type, as it's now a supported as vector with no id. Need to set all filament-setting id to 0, as the gui need them to update the first elt only (and always that one) for field <-> config pipe: - config now have get_any(id) and set_any(any, id). No need to special optiongroup or gui method. - any will carry a type that is in sync with the config type and the id. - if id is set or type is scalar, the any is scalara. else the any is a vector of scalar. - fields are responsible to cast the any correctly and to set it correctly. no more mess of wxstring & double --- resources/ui_layout/default/filament.ui | 140 +-- resources/ui_layout/default/print.ui | 1 + resources/ui_layout/example/filament.ui | 138 +-- src/PrusaSlicer.cpp | 4 +- src/libslic3r/Config.cpp | 10 +- src/libslic3r/Config.hpp | 352 +++++--- src/libslic3r/Extruder.cpp | 36 +- src/libslic3r/Extruder.hpp | 2 +- src/libslic3r/Fill/Fill.cpp | 4 +- src/libslic3r/Format/3mf.cpp | 16 +- src/libslic3r/Format/CWS.cpp | 2 +- src/libslic3r/Format/SL1.cpp | 4 +- src/libslic3r/Format/SLAArchive.cpp | 16 +- src/libslic3r/GCodeWriter.cpp | 22 +- src/libslic3r/GCodeWriter.hpp | 2 +- src/libslic3r/Model.cpp | 4 +- src/libslic3r/MultiMaterialSegmentation.cpp | 2 +- src/libslic3r/PlaceholderParser.cpp | 20 +- src/libslic3r/PrintApply.cpp | 2 +- src/libslic3r/PrintBase.cpp | 3 +- src/libslic3r/PrintConfig.cpp | 67 +- src/libslic3r/PrintObject.cpp | 8 +- src/libslic3r/SLA/Rotfinder.cpp | 4 +- src/libslic3r/SLAPrint.cpp | 86 +- src/libslic3r/SLAPrintSteps.cpp | 52 +- src/libslic3r/Slicing.cpp | 2 +- src/slic3r/GUI/BedShapeDialog.cpp | 12 +- src/slic3r/GUI/CalibrationBridgeDialog.cpp | 2 +- src/slic3r/GUI/CalibrationFlowDialog.cpp | 2 +- .../GUI/CalibrationOverBridgeDialog.cpp | 7 +- .../GUI/CalibrationRetractionDialog.cpp | 9 +- src/slic3r/GUI/ConfigManipulation.cpp | 32 +- src/slic3r/GUI/Field.cpp | 808 ++++++++++++------ src/slic3r/GUI/Field.hpp | 76 +- src/slic3r/GUI/GLCanvas3D.cpp | 4 +- src/slic3r/GUI/GUI.cpp | 139 +-- src/slic3r/GUI/GUI.hpp | 2 +- src/slic3r/GUI/GUI_ObjectLayers.cpp | 2 +- src/slic3r/GUI/GUI_Preview.cpp | 10 +- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 6 +- src/slic3r/GUI/OptionsGroup.cpp | 189 +--- src/slic3r/GUI/OptionsGroup.hpp | 5 +- src/slic3r/GUI/PhysicalPrinterDialog.cpp | 6 +- src/slic3r/GUI/Plater.cpp | 8 +- src/slic3r/GUI/ScriptExecutor.cpp | 122 +-- src/slic3r/GUI/Tab.cpp | 19 +- src/slic3r/GUI/Tab.hpp | 2 +- src/slic3r/GUI/UnsavedChangesDialog.cpp | 4 +- 48 files changed, 1341 insertions(+), 1124 deletions(-) diff --git a/resources/ui_layout/default/filament.ui b/resources/ui_layout/default/filament.ui index 845f5e3773b..eeaa1973a75 100644 --- a/resources/ui_layout/default/filament.ui +++ b/resources/ui_layout/default/filament.ui @@ -1,97 +1,97 @@ - +#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 + 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/resources/ui_layout/default/print.ui b/resources/ui_layout/default/print.ui index 196981ae37f..a1b54e6b2b7 100644 --- a/resources/ui_layout/default/print.ui +++ b/resources/ui_layout/default/print.ui @@ -202,6 +202,7 @@ group:sidetext_width$5:Infill angle 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 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 697fcb566a0..63de0d2d51b 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -170,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; @@ -203,7 +203,7 @@ 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); params.angle = (is_denser) ? 0 : compute_fill_angle(region_config, layerm.layer()->id()); params.can_angle_cross = region_config.fill_angle_cross; 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/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/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 4aada338432..d5bbbf7f7fd 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); @@ -2294,7 +2293,7 @@ void PrintConfigDef::init_fff_params() def->max = 360; def->full_width = true; def->mode = comExpert | comSuSi; - def->set_default_value(new ConfigOptionFloats{}); + def->set_default_value(new ConfigOptionFloats(0.)); def = this->add("fill_density", coPercent); def->gui_type = ConfigOptionDef::GUIType::f_enum_open; @@ -4217,7 +4216,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; @@ -6421,12 +6419,35 @@ void PrintConfigDef::init_fff_params() def->full_label = it_opt->second.full_label; def->tooltip = it_opt->second.tooltip; def->sidetext = it_opt->second.sidetext; - def->mode = it_opt->second.mode; + 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->values.push_back(ConfigOptionBoolsNullable::NIL_VALUE()); + def->set_default_value(opt); + break; + } + case coFloats: { + ConfigOptionFloatsNullable *opt = new ConfigOptionFloatsNullable(it_opt->second.default_value.get()->get_float()); + opt->values.push_back(ConfigOptionFloatsNullable::NIL_VALUE()); + def->set_default_value(opt); + break; + } + case coPercents: { + ConfigOptionPercentsNullable *opt = new ConfigOptionPercentsNullable(it_opt->second.default_value.get()->get_float()); + opt->values.push_back(ConfigOptionPercentsNullable::NIL_VALUE()); + def->set_default_value(opt); + break; + } + case coFloatsOrPercents: { + ConfigOptionFloatsOrPercentsNullable*opt = new ConfigOptionFloatsOrPercentsNullable( + static_cast(it_opt->second.default_value.get())->get_at(0)); + opt->values.push_back(ConfigOptionFloatsOrPercentsNullable::NIL_VALUE()); + def->set_default_value(opt); + break; + } default: assert(false); } } @@ -7963,7 +7984,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"; } @@ -8126,10 +8147,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; } @@ -8185,7 +8206,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; @@ -8195,7 +8216,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; } @@ -8204,15 +8225,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), @@ -8227,9 +8248,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), @@ -8255,13 +8276,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. @@ -8275,7 +8296,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/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 4c9e9be0eda..da8375dd980 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1297,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 @@ -2541,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 a1a5c876230..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; @@ -434,7 +434,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) //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 @@ -511,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/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/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 7ad55dd2765..51dd05d57ed 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -49,9 +49,6 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co case ConfigOptionDef::GUIType::legend: // StaticText m_fields.emplace(id, StaticText::Create(this->ctrl_parent(), opt, id)); break; - case ConfigOptionDef::GUIType::one_string: - m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); - break; default: switch (opt.type) { case coFloatOrPercent: @@ -64,12 +61,22 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co case coStrings: m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); break; + case coBools: + if (id.find('#') == std::string::npos) { + // string field with vector serialization + m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); + break; + } case coBool: - case coBools: m_fields.emplace(id, CheckBox::Create(this->ctrl_parent(), opt, id)); break; + case coInts: + if (id.find('#') == std::string::npos) { + // string field with vector serialization + m_fields.emplace(id, TextCtrl::Create(this->ctrl_parent(), opt, id)); + break; + } case coInt: - case coInts: m_fields.emplace(id, SpinCtrl::Create(this->ctrl_parent(), opt, id)); break; case coEnum: @@ -658,7 +665,7 @@ void ConfigOptionsGroup::on_change_OG(const t_config_option_key& opt_id, const b const std::string &opt_key = itOption.first; int opt_index = itOption.second; - this->change_opt_value(opt_key, value, opt_index == -1 ? 0 : opt_index); + this->change_opt_value(opt_key, value, opt_index); } OptionsGroup::on_change_OG(opt_id, value); @@ -726,8 +733,8 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, if (initial_conf.has(dep_key) && edited_conf.has(dep_key)) { ConfigOption* conf_opt = initial_conf.option(dep_key)->clone(); // update the field - tab->set_value(dep_key, get_config_value(initial_conf, dep_key)); - tab->on_value_change(dep_key, conf_opt->getAny()); + tab->set_value(dep_key, initial_conf.option(dep_key)->get_any()); + tab->on_value_change(dep_key, conf_opt->get_any()); } } } @@ -740,14 +747,14 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, } else if (it_opt_map == m_opt_map.end() || // This option doesn't have corresponded field is_option_without_field(opt_key) ) { - value = get_config_value(config, opt_key); + value = config.option(opt_key)->get_any(); this->change_opt_value(opt_key, value); return; } else { auto opt_id = it_opt_map->first; std::string opt_short_key = m_opt_map.at(opt_id).first; int opt_index = m_opt_map.at(opt_id).second; - value = get_config_value(config, opt_short_key, opt_index); + value = config.option(opt_short_key)->get_any(opt_index); } if(set_value(opt_key, value)) @@ -772,7 +779,7 @@ void ConfigOptionsGroup::reload_config() // index in the vector option, zero for scalars int opt_index = kvp.second.second; const ConfigOptionDef &option = m_options.at(opt_id).opt; - this->set_value(opt_id, config_value(opt_key, opt_index, option.gui_flags == "serialized")); + this->set_value(opt_id, m_config->option(opt_key)->get_any(opt_index)); } update_script_presets(); } @@ -964,163 +971,6 @@ void ConfigOptionsGroup::refresh() custom_ctrl->Refresh(); } -boost::any ConfigOptionsGroup::config_value(const std::string& opt_key, int opt_index, bool deserialize) { - - if (deserialize) { - // Want to edit a vector value(currently only multi - strings) in a single edit box. - // Aggregate the strings the old way. - // Currently used for the post_process config value only. - if (opt_index != -1) - throw Slic3r::OutOfRange("Can't deserialize option indexed value"); -// return join(';', m_config->get(opt_key)}); - return get_config_value(*m_config, opt_key); - } - else { -// return opt_index == -1 ? m_config->get(opt_key) : m_config->get_at(opt_key, opt_index); - return get_config_value(*m_config, opt_key, opt_index); - } -} - -boost::any ConfigOptionsGroup::get_config_value(const DynamicConfig& config, const std::string& opt_key, int opt_index /*= -1*/) -{ - size_t idx = opt_index == -1 ? 0 : opt_index; - - boost::any ret; - wxString text_value = wxString(""); - const ConfigOptionDef* opt = config.def()->get(opt_key); - - if (opt->nullable) - { - switch (opt->type) - { - case coPercents: - case coFloats: { - if (config.option(opt_key)->is_nil()) - ret = _(L("N/A")); - else { - double val = opt->type == coFloats ? - config.option(opt_key)->get_at(idx) : - config.option(opt_key)->get_at(idx); - ret = double_to_string(val, opt->precision); } - } - break; - case coFloatsOrPercents: { - if (config.option(opt_key)->is_nil()) - ret = _(L("N/A")); - else { - FloatOrPercent float_percent = config.option(opt_key)->get_at(idx); - text_value = double_to_string(float_percent.value, opt->precision); - if (float_percent.percent) - text_value += "%"; - ret = text_value; - } - } - break; - case coBools: - ret = config.option(opt_key)->values[idx]; - break; - case coInts: - ret = config.option(opt_key)->get_at(idx); - break; - case coPoints: - default: - break; - } - return ret; - } - - switch (opt->type) { - case coFloatOrPercent:{ - const auto &value = *config.option(opt_key); - - text_value = double_to_string(value.value, opt->precision); - if (value.percent) - text_value += "%"; - - ret = text_value; - break; - } - case coFloatsOrPercents:{ - const ConfigOptionFloatsOrPercents &value = *config.option(opt_key); - - text_value = double_to_string(value.get_at(idx).value, opt->precision); - if (value.get_at(idx).percent) - text_value += "%"; - - ret = text_value; - break; - } - case coPercent:{ - double val = config.option(opt_key)->value; - text_value = double_to_string(val, opt->precision); - ret = text_value; - } - break; - case coPercents: - case coFloats: - case coFloat:{ - double val = opt->type == coFloats ? - config.opt_float(opt_key, idx) : - opt->type == coFloat ? config.opt_float(opt_key) : - config.option(opt_key)->get_at(idx); - ret = double_to_string(val, opt->precision); - } - break; - case coString: - ret = from_u8(config.opt_string(opt_key)); - break; - case coStrings: - if (opt_key == "compatible_printers" || opt_key == "compatible_prints" || opt_key == "gcode_substitutions") { - ret = config.option(opt_key)->values; - break; - } - if (opt_key == "filament_ramming_parameters") { - ret = config.opt_string(opt_key, static_cast(idx)); - break; - } - if (config.option(opt_key)->values.empty()) - ret = text_value; - else if (opt->gui_flags == "serialized") { - std::vector values = config.option(opt_key)->values; - if (!values.empty() && !values[0].empty()) - for (auto el : values) - text_value += el + ";"; - ret = text_value; - } - else - ret = from_u8(config.opt_string(opt_key, static_cast(idx))); - break; - case coBool: - ret = config.opt_bool(opt_key); - break; - case coBools: - ret = config.opt_bool(opt_key, idx); - break; - case coInt: - ret = config.opt_int(opt_key); - break; - case coInts: - ret = config.opt_int(opt_key, idx); - break; - case coEnum: - ret = config.option(opt_key)->getInt(); - break; - case coPoint: - ret = config.option(opt_key)->value; - break; - case coPoints: - if (opt_key == "bed_shape") - ret = config.option(opt_key)->values; - else - ret = config.option(opt_key)->get_at(idx); - break; - case coNone: - default: - break; - } - return ret; -} - Field* ConfigOptionsGroup::get_fieldc(const t_config_option_key& opt_key, int opt_index) { Field* field = get_field(opt_key); @@ -1155,7 +1005,8 @@ std::pair ConfigOptionsGroup::get_custom_ctrl_with_blinki void ConfigOptionsGroup::change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) { - Slic3r::GUI::change_opt_value(const_cast(*m_config), opt_key, value, opt_index); + //Slic3r::GUI::change_opt_value(const_cast(*m_config), opt_key, value, opt_index); + const_cast(*m_config).option(opt_key)->set_any(value, opt_index); if (m_modelconfig) m_modelconfig->touch(); } diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 7eca3b9231e..da388b6867a 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -309,9 +309,10 @@ class ConfigOptionsGroup: public OptionsGroup { void msw_rescale(); void sys_color_changed(); void refresh(); - boost::any config_value(const std::string& opt_key, int opt_index, bool deserialize); + //call optionconfig->get_any() + //boost::any config_value(const std::string& opt_key, int opt_index, bool deserialize); // return option value from config - boost::any get_config_value(const DynamicConfig& config, const std::string& opt_key, int opt_index = -1); + //boost::any get_config_value(const DynamicConfig& config, const std::string& opt_key, int opt_index = -1); Field* get_fieldc(const t_config_option_key& opt_key, int opt_index); std::pair get_custom_ctrl_with_blinking_ptr(const t_config_option_key& opt_key, int opt_index/* = -1*/); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 328df639204..9a4105017c6 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -307,7 +307,7 @@ void PhysicalPrinterDialog::update_printers() boost::any any_string_type = std::string(""); auto value_idx = std::find(slugs.begin(), slugs.end(), m_config->opt("printhost_port")->value); if ((val.empty() || (any_string_type.type() == val.type() && boost::any_cast(val) == "")) && !slugs.empty() && value_idx == slugs.end()) { - change_opt_value(*m_config, "printhost_port", slugs[0]); + m_config->option("printhost_port")->set_any(slugs[0]); // change_opt_value(*m_config, "printhost_port", slugs[0]); choice->set_value(slugs[0], false); } else if (value_idx != slugs.end()) { choice->set_value(m_config->option("printhost_port")->value, false); @@ -325,7 +325,7 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr if(opt_key == "printhost_client_cert_enabled") this->m_show_cert_fields = boost::any_cast(value); if (!this->m_show_cert_fields && !m_config->opt_string("printhost_client_cert").empty()) { - change_opt_value(*m_config, "printhost_client_cert", std::string("")); + m_config->option("printhost_client_cert")->set_any(std::string("")); //change_opt_value(*m_config, "printhost_client_cert", std::string("")); //change_opt_value(*m_config, "printhost_client_cert_password", ""); m_config->set_deserialize_strict("printhost_client_cert_password", ""); } @@ -691,7 +691,7 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) else if ((printer_change && !all_presets_are_from_mk3_family) || (!all_presets_are_from_mk3_family && m_config->option>("host_type")->value == htPrusaLink)) set_to_choice_and_config(htOctoPrint); else - choice->set_value(m_config->option("host_type")->getInt()); + choice->set_value(m_config->option("host_type")->get_int()); had_all_mk3 = all_presets_are_from_mk3_family; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9d09ffb23f9..c07651e9219 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1281,11 +1281,11 @@ void Sidebar::update_sliced_info_sizer() wxString str_total_cost = "N/A"; DynamicPrintConfig* cfg = wxGetApp().get_tab(Preset::TYPE_SLA_MATERIAL)->get_config(); - if (cfg->option("bottle_cost")->getFloat() > 0.0 && - cfg->option("bottle_volume")->getFloat() > 0.0) + if (cfg->option("bottle_cost")->get_float() > 0.0 && + cfg->option("bottle_volume")->get_float() > 0.0) { - double material_cost = cfg->option("bottle_cost")->getFloat() / - cfg->option("bottle_volume")->getFloat(); + double material_cost = cfg->option("bottle_cost")->get_float() / + cfg->option("bottle_volume")->get_float(); str_total_cost = wxString::Format("%.3f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000); } p->sliced_info->SetTextAndShow(siCost, str_total_cost, "Cost"); diff --git a/src/slic3r/GUI/ScriptExecutor.cpp b/src/slic3r/GUI/ScriptExecutor.cpp index a84ac04ca60..728691a8b71 100644 --- a/src/slic3r/GUI/ScriptExecutor.cpp +++ b/src/slic3r/GUI/ScriptExecutor.cpp @@ -86,9 +86,9 @@ bool as_get_bool(std::string& key) throw NoDefinitionException("error, can't find bool option " + key); if (opt->is_vector()) { const ConfigOptionVectorBase* vector = static_cast(opt); - return vector->getFloat(0) != 0; + return vector->get_float(0) != 0; } else { - return opt->getBool(); + return opt->get_bool(); } } void as_set_bool(std::string& key, bool b) @@ -113,9 +113,9 @@ int32_t as_get_int(std::string& key) throw NoDefinitionException("error, can't find int option " + key); if (opt->is_vector()) { const ConfigOptionVectorBase* vector = static_cast(opt); - return (int32_t)vector->getFloat(0); + return (int32_t)vector->get_float(0); } else { - return (int32_t)(opt->getInt()); + return (int32_t)(opt->get_int()); } } void as_set_int(std::string& key, int val) @@ -149,7 +149,7 @@ void as_set_int(std::string& key, int val) // } // if (value >= 0 && value < def->enum_values.size()) { ConfigOption* copy = result.second->clone(); - copy->setInt(val); + copy->set_enum_int(val); conf.set_key_value(key, copy); // return; // } @@ -164,9 +164,9 @@ float as_get_float(std::string& key) throw NoDefinitionException("error, can't find float option " + key); if (opt->is_vector()) { const ConfigOptionVectorBase* vector = static_cast(opt); - return (float)vector->getFloat(0); + return (float)vector->get_float(0); } else { - return (float)(opt->getFloat()); + return (float)(opt->get_float()); } } @@ -187,7 +187,7 @@ void as_set_float(std::string& key, float f_val) DynamicPrintConfig& conf = current_script->to_update()[result.first->type()]; if (result.second->type() == ConfigOptionType::coFloat) { - double old_value = result.second->getFloat(); + double old_value = result.second->get_float(); double new_val = round(f_val); // only update if difference is significant if (std::abs(old_value - new_val) / std::abs(old_value) < 0.0000001) @@ -207,7 +207,7 @@ void as_set_float(std::string& key, float f_val) } else if (result.second->type() == ConfigOptionType::coPercent) { double percent_f = floor(f_val * 100000. + 0.5) / 1000.; // only update if difference is significant - double old_value = result.second->getFloat(); + double old_value = result.second->get_float(); if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; conf.set_key_value(key, new ConfigOptionPercent(percent_f)); @@ -226,7 +226,7 @@ void as_set_float(std::string& key, float f_val) double new_val = round(f_val); if (!static_cast(result.second)->percent) { // only update if difference is significant - double old_value = result.second->getFloat(); + double old_value = result.second->get_float(); if (std::abs(old_value - new_val) / std::abs(old_value) < 0.0000001) new_val = old_value; } @@ -263,7 +263,7 @@ void as_set_percent(std::string& key, float f_val) DynamicPrintConfig& conf = current_script->to_update()[result.first->type()]; if (result.second->type() == ConfigOptionType::coFloat) { // only update if difference is significant - double old_value = result.second->getFloat() * 100; + double old_value = result.second->get_float() * 100; if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; // don't return int these check, as it can escpae a refresh of the scripted widget conf.set_key_value(key, new ConfigOptionFloat(percent_f / 100.)); @@ -279,7 +279,7 @@ void as_set_percent(std::string& key, float f_val) conf.set_key_value(key, new_opt); } else if (result.second->type() == ConfigOptionType::coPercent) { // only update if difference is significant - double old_value = get_coll(key).second->getFloat(); + double old_value = get_coll(key).second->get_float(); if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; conf.set_key_value(key, new ConfigOptionPercent(percent_f)); @@ -296,7 +296,7 @@ void as_set_percent(std::string& key, float f_val) } else if (result.second->type() == ConfigOptionType::coFloatOrPercent) { if (static_cast(result.second)->percent) { // only update if difference is significant - double old_value = result.second->getFloat(); + double old_value = result.second->get_float(); if (std::abs(old_value - percent_f) / std::abs(old_value) < 0.0000001) percent_f = old_value; } @@ -346,7 +346,7 @@ void as_set_string(std::string& key, std::string& val) for (; idx < def->enum_values.size() && def->enum_values[idx] != val; idx++) {} if (idx >= 0 && idx < def->enum_values.size()) { ConfigOption* copy = result.second->clone(); - copy->setInt(idx); + copy->set_enum_int(idx); conf.set_key_value(key, copy); } } @@ -788,33 +788,35 @@ void ScriptContainer::call_script_function_set(const ConfigOptionDef& def, const ctx->Prepare(func); std::string str_arg; switch (def.type) { - case coBool: - case coBools: ctx->SetArgByte(0, boost::any_cast(value)); break; + case coBools: ctx->SetArgByte(0, boost::any_cast(value)); break; + case coBool: ctx->SetArgByte(0, boost::any_cast(value)); break; case coInt: - case coInts: ctx->SetArgDWord(0, boost::any_cast(value)); break; + case coInts: ctx->SetArgDWord(0, boost::any_cast(value)); break; case coPercent: case coPercents: case coFloat: case coFloats: ctx->SetArgFloat(0, (float)boost::any_cast(value)); break; case coFloatOrPercent: case coFloatsOrPercents: { - std::string flOrPercent = boost::any_cast(value); - float val = 0; - bool is_percent = false; - if (flOrPercent[flOrPercent.size() - 1] == '%') { - flOrPercent = flOrPercent.substr(0, flOrPercent.size() - 1); - val = std::stof(flOrPercent); - is_percent = true; - } else { - val = std::stof(flOrPercent); - } - ctx->SetArgDWord(0, boost::any_cast((float)val)); - ctx->SetArgByte(1, boost::any_cast(is_percent)); + FloatOrPercent fl_percent = boost::any_cast(value); + ctx->SetArgDWord(0, boost::any_cast((float) fl_percent.value)); + ctx->SetArgByte(1, boost::any_cast(fl_percent.percent)); break; } case coPoint: - case coPoints: { ctx->SetArgFloat(0, (float)boost::any_cast(value)); ctx->SetArgFloat(1, (float)boost::any_cast(value)); break; } //FIXME - case coPoint3: { ctx->SetArgFloat(0, (float)boost::any_cast(value)); ctx->SetArgFloat(1, (float)boost::any_cast(value)); ctx->SetArgFloat(2, (float)boost::any_cast(value)); break; } + case coPoints: { + Vec2d vec = boost::any_cast(value); + ctx->SetArgFloat(0, (float) vec.x()); + ctx->SetArgFloat(1, (float) vec.y()); + break; + } + case coPoint3: { + Vec3d vec = boost::any_cast(value); + ctx->SetArgFloat(0, (float) vec.x()); + ctx->SetArgFloat(1, (float) vec.y()); + ctx->SetArgFloat(2, (float) vec.z()); + break; + } case coString: case coStrings: { str_arg = boost::any_cast(value); @@ -868,7 +870,7 @@ void ScriptContainer::call_script_function_set(const ConfigOptionDef& def, const for (const auto& data : to_update) { Tab* tab = wxGetApp().get_tab(data.first); for (auto opt_key : data.second.keys()) { - tab->on_value_change(opt_key, data.second.option(opt_key)->getAny()); + tab->on_value_change(opt_key, data.second.option(opt_key)->get_any()); } } // refresh the field if needed @@ -923,7 +925,7 @@ bool ScriptContainer::call_script_function_reset(const ConfigOptionDef& def) for (const auto& data : to_update) { Tab* tab = wxGetApp().get_tab(data.first); for (auto opt_key : data.second.keys()) { - tab->on_value_change(opt_key, data.second.option(opt_key)->getAny()); + tab->on_value_change(opt_key, data.second.option(opt_key)->get_any()); } } // refresh the field if needed @@ -1028,55 +1030,71 @@ boost::any ScriptContainer::call_script_function_get_value(const ConfigOptionDef int res = ctx->Execute(); int32_t ret_int; float ret_float; - boost::any field_val; boost::any opt_val; switch (def.type) { case coBool: - case coBools: { ret_int = ctx->GetReturnDWord(); field_val = uint8_t(ret_int < 0 ? 2 : ret_int); opt_val = uint8_t((ret_int > 0)?1:0); break; } //CheckBox + case coBools: { + ret_int = ctx->GetReturnDWord(); + opt_val = uint8_t(ret_int < 0 ? 2 : ret_int); + break; + } case coInt: - case coInts: { ret_int = ctx->GetReturnDWord(); field_val = int32_t(ret_int); opt_val = int(ret_int); break; } //SpinCtrl + case coInts: { + ret_int = ctx->GetReturnDWord(); + opt_val = int32_t(ret_int); + break; + } // SpinCtrl case coString: - case coStrings: { field_val = from_u8(ret_str); opt_val = ret_str; break; } //TextCtrl + case coStrings: { + opt_val = ret_str; + break; + } // TextCtrl case coPercent: - case coPercents: ret_percent = true; + case coPercents: case coFloat: - case coFloats: opt_val = double(ctx->GetReturnFloat()); + case coFloats: { + opt_val = double(ctx->GetReturnFloat()); + break; + } case coFloatOrPercent: case coFloatsOrPercents: { ret_float = ctx->GetReturnFloat(); - wxString ret_wstring = double_to_string(ret_float); - if (ret_percent) - ret_wstring += '%'; - field_val = ret_wstring; //TextCtrl - if (opt_val.empty()) { opt_val = ret_wstring.ToStdString(); } + opt_val = FloatOrPercent{ret_float, ret_percent}; break; } case coPoint: - case coPoints: { ret_float = ctx->GetReturnFloat(); field_val = Vec2d{ ret_float, ret_float }; opt_val = double(ctx->GetReturnFloat()); break; } //FIXME PointCtrl - case coPoint3: { ret_float = ctx->GetReturnFloat(); field_val = Vec3d{ ret_float, ret_float, ret_float }; opt_val = double(ctx->GetReturnFloat()); break; } + case coPoints: { + double pt_x = ctx->GetReturnFloat(); + opt_val = Vec2d{pt_x, pt_x}; // FIXME + break; + } // FIXME PointCtrl + case coPoint3: { + double pt_x = ctx->GetReturnFloat(); + opt_val = Vec3d{pt_x, pt_x, pt_x}; + break; + } case coEnum: { ret_int = ctx->GetReturnDWord(); if (ret_int >= 0 && ret_int < def.enum_values.size()) { - field_val = int32_t(ret_int); + opt_val = int32_t(ret_int); } else { - field_val = int32_t(0); + opt_val = int32_t(0); for (size_t i = 0; i < def.enum_values.size(); i++) { if (ret_str == def.enum_values[i]) - field_val = int32_t(i); + opt_val = int32_t(i); } } - opt_val = field_val; break; //Choice } } if (m_need_refresh) { refresh(def, opt_val); } - if (field_val.empty()) { + if (opt_val.empty()) { std::cout << "Error nullptr for script\n"; } - return field_val; + return opt_val; } void ScriptContainer::refresh(const ConfigOptionDef& def, boost::any value) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f20beba2736..4a07e187cb9 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -820,7 +820,7 @@ void Tab::init_options_list() template void add_correct_opts_to_options_list(const std::string &opt_key, std::map& map, Tab *tab, const int& value) { - T *opt_cur = static_cast(tab->m_config->option(opt_key)); + T *opt_cur = static_cast(tab->get_config()->option(opt_key)); for (size_t i = 0; i < opt_cur->values.size(); i++) map.emplace(opt_key + "#" + std::to_string(i), value); } @@ -1253,7 +1253,8 @@ void Tab::toggle_option(const std::string& opt_key, bool toggle, int opt_index/* // and value can be some random value because in this case it will not been used void Tab::load_key_value(const std::string& opt_key, const boost::any& value, bool saved_value /*= false*/) { - if (!saved_value) change_opt_value(*m_config, opt_key, value); + if (!saved_value) + m_config->option(opt_key)->set_any(value, -1); // change_opt_value(*m_config, opt_key, value); // Mark the print & filament enabled if they are compatible with the currently selected preset. if (opt_key == "compatible_printers" || opt_key == "compatible_prints") { // Don't select another profile if this profile happens to become incompatible. @@ -1328,7 +1329,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) // update unscripted freq params Field* field = og_freq_chng_params->get_field(opt_key); if (field) { - boost::any val = og_freq_chng_params->get_config_value(*m_config, opt_key); + boost::any val = m_config->option(opt_key)->get_any(field->m_opt_idx); field->set_value(val, false); } @@ -1885,15 +1886,15 @@ std::vector Tab::create_pages(std::string setting_type_nam DynamicPrintConfig new_conf = *m_config; if (opt_key == "bottle_volume") { - double new_bottle_weight = boost::any_cast(value) / (new_conf.option("material_density")->getFloat() * 1000); + double new_bottle_weight = boost::any_cast(value) / (new_conf.option("material_density")->get_float() * 1000); new_conf.set_key_value("bottle_weight", new ConfigOptionFloat(new_bottle_weight)); } if (opt_key == "bottle_weight") { - double new_bottle_volume = boost::any_cast(value)*(new_conf.option("material_density")->getFloat() * 1000); + double new_bottle_volume = boost::any_cast(value)*(new_conf.option("material_density")->get_float() * 1000); new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume)); } if (opt_key == "material_density") { - double new_bottle_volume = new_conf.option("bottle_weight")->getFloat() * boost::any_cast(value) * 1000; + double new_bottle_volume = new_conf.option("bottle_weight")->get_float() * boost::any_cast(value) * 1000; new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume)); } @@ -2732,7 +2733,7 @@ void TabPrint::update() { const Preset& selected_preset = m_preset_bundle->fff_prints.get_selected_preset(); bool is_user_and_saved_preset = !selected_preset.is_system && !selected_preset.is_dirty; - bool support_material_overhangs_queried = m_config->opt_bool("support_material") && m_config->option("overhangs_width_speed")->getFloat() == 0; + bool support_material_overhangs_queried = m_config->opt_bool("support_material") && m_config->option("overhangs_width_speed")->get_float() == 0; m_config_manipulation.initialize_support_material_overhangs_queried(is_user_and_saved_preset && support_material_overhangs_queried); } @@ -2800,11 +2801,11 @@ PageShp TabFilament::create_filament_overrides_page() { Line line {"",""}; if (opt_key == "filament_retract_lift_above" || opt_key == "filament_retract_lift_below") { - Option opt = optgroup->get_option_and_register(opt_key); + Option opt = optgroup->get_option_and_register(opt_key, 0); opt.opt.label = opt.opt.get_full_label(); line = optgroup->create_single_option_line(opt); } else { - line = optgroup->create_single_option_line(optgroup->get_option_and_register(opt_key)); + line = optgroup->create_single_option_line(optgroup->get_option_and_register(opt_key, 0)); } line.near_label_widget = [this, optgroup, opt_key, opt_index](wxWindow* parent) { diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index b3a941af049..739c31f02dc 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -359,11 +359,11 @@ class Tab: public wxPanel bool m_page_switch_running = false; bool m_page_switch_planned = false; + DynamicPrintConfig* m_config; public: PresetBundle* m_preset_bundle; bool m_show_btn_incompatible_presets = false; PresetCollection* m_presets = nullptr; - DynamicPrintConfig* m_config; ogStaticText* m_parent_preset_description_line = nullptr; ScalableButton* m_detach_preset_btn = nullptr; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index cb0736e4e3c..96725c07d30 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -1039,7 +1039,7 @@ wxString get_string_from_enum(const std::string& opt_key, const DynamicPrintConf { const ConfigOptionDef& def = config.def()->options.at(opt_key); const std::vector& names = def.enum_labels.empty() ? def.enum_values : def.enum_labels; - int val = config.option(opt_key)->getInt(); + int val = config.option(opt_key)->get_int(); // if it doesn't use all list declared in PrintConfig.hpp. // So we should "convert" val to the correct one @@ -1119,7 +1119,7 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& return _L("Undef"); } case coPercent: - return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->getFloat())).str()); + return from_u8((boost::format("%1%%%") % int(config.optptr(opt_key)->get_float())).str()); case coPercents: { if (is_nullable) { auto values = config.opt(opt_key);