2017-11-02 15:21:34 +00:00
# include <cassert>
2020-09-14 14:27:55 +00:00
# include "Exception.hpp"
2017-09-19 11:55:48 +00:00
# include "Preset.hpp"
2020-08-08 15:03:20 +00:00
# include "AppConfig.hpp"
2017-09-19 11:55:48 +00:00
2018-12-12 18:02:18 +00:00
# ifdef _MSC_VER
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include <Windows.h>
# endif /* _MSC_VER */
2020-06-16 14:58:41 +00:00
// instead of #include "slic3r/GUI/I18N.hpp" :
# ifndef L
// !!! If you needed to translate some string,
// !!! please use _L(string)
// !!! _() - is a standard wxWidgets macro to translate
2021-02-24 08:22:31 +00:00
// !!! L() is used only for marking localizable string
2020-06-16 14:58:41 +00:00
// !!! It will be used in "xgettext" to create a Locating Message Catalog.
# define L(s) s
# endif /* L */
2019-01-30 14:22:17 +00:00
# include <algorithm>
2017-09-19 11:55:48 +00:00
# include <fstream>
2018-04-12 18:04:48 +00:00
# include <stdexcept>
2019-02-08 09:33:07 +00:00
# include <unordered_map>
2018-04-12 18:04:48 +00:00
# include <boost/format.hpp>
2017-10-02 15:35:00 +00:00
# include <boost/filesystem.hpp>
2018-04-11 11:12:08 +00:00
# include <boost/filesystem/fstream.hpp>
2020-06-26 07:58:39 +00:00
# include <boost/algorithm/string.hpp>
2017-09-19 11:55:48 +00:00
# include <boost/algorithm/string/predicate.hpp>
2017-10-25 10:53:31 +00:00
# include <boost/nowide/cenv.hpp>
2018-12-12 18:02:18 +00:00
# include <boost/nowide/convert.hpp>
2017-10-26 15:17:39 +00:00
# include <boost/nowide/cstdio.hpp>
2017-10-25 10:53:31 +00:00
# include <boost/nowide/fstream.hpp>
# include <boost/property_tree/ini_parser.hpp>
# include <boost/property_tree/ptree.hpp>
2017-10-26 15:17:39 +00:00
# include <boost/locale.hpp>
2018-05-01 08:35:07 +00:00
# include <boost/log/trivial.hpp>
2017-10-25 10:53:31 +00:00
2020-06-16 14:58:41 +00:00
# include "libslic3r.h"
# include "Utils.hpp"
# include "PlaceholderParser.hpp"
2017-10-25 10:53:31 +00:00
2018-04-11 11:12:08 +00:00
using boost : : property_tree : : ptree ;
2017-09-19 11:55:48 +00:00
namespace Slic3r {
2018-04-11 11:12:08 +00:00
ConfigFileType guess_config_file_type ( const ptree & tree )
2017-11-01 18:30:05 +00:00
{
size_t app_config = 0 ;
size_t bundle = 0 ;
size_t config = 0 ;
2018-04-11 11:12:08 +00:00
for ( const ptree : : value_type & v : tree ) {
2017-11-01 18:30:05 +00:00
if ( v . second . empty ( ) ) {
if ( v . first = = " background_processing " | |
v . first = = " last_output_path " | |
v . first = = " no_controller " | |
v . first = = " no_defaults " )
+ + app_config ;
else if ( v . first = = " nozzle_diameter " | |
v . first = = " filament_diameter " )
+ + config ;
} else if ( boost : : algorithm : : starts_with ( v . first , " print: " ) | |
boost : : algorithm : : starts_with ( v . first , " filament: " ) | |
boost : : algorithm : : starts_with ( v . first , " printer: " ) | |
v . first = = " settings " )
+ + bundle ;
else if ( v . first = = " presets " ) {
+ + app_config ;
+ + bundle ;
} else if ( v . first = = " recent " ) {
for ( auto & kvp : v . second )
if ( kvp . first = = " config_directory " | | kvp . first = = " skein_directory " )
+ + app_config ;
}
}
return ( app_config > bundle & & app_config > config ) ? CONFIG_FILE_TYPE_APP_CONFIG :
( bundle > config ) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG ;
}
2018-04-11 11:12:08 +00:00
VendorProfile VendorProfile : : from_ini ( const boost : : filesystem : : path & path , bool load_all )
{
ptree tree ;
boost : : filesystem : : ifstream ifs ( path ) ;
boost : : property_tree : : read_ini ( ifs , tree ) ;
return VendorProfile : : from_ini ( tree , path , load_all ) ;
}
2019-02-08 09:33:07 +00:00
static const std : : unordered_map < std : : string , std : : string > pre_family_model_map { {
{ " MK3 " , " MK3 " } ,
{ " MK3MMU2 " , " MK3 " } ,
{ " MK2.5 " , " MK2.5 " } ,
{ " MK2.5MMU2 " , " MK2.5 " } ,
{ " MK2S " , " MK2 " } ,
{ " MK2SMM " , " MK2 " } ,
{ " SL1 " , " SL1 " } ,
} } ;
2018-04-11 11:12:08 +00:00
VendorProfile VendorProfile : : from_ini ( const ptree & tree , const boost : : filesystem : : path & path , bool load_all )
{
static const std : : string printer_model_key = " printer_model: " ;
2019-04-12 14:18:46 +00:00
static const std : : string filaments_section = " default_filaments " ;
static const std : : string materials_section = " default_sla_materials " ;
2018-04-11 11:12:08 +00:00
const std : : string id = path . stem ( ) . string ( ) ;
2018-04-12 18:04:48 +00:00
if ( ! boost : : filesystem : : exists ( path ) ) {
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( ( boost : : format ( " Cannot load Vendor Config Bundle `%1%`: File not found: `%2%`. " ) % id % path ) . str ( ) ) ;
2018-04-12 18:04:48 +00:00
}
2018-04-11 11:12:08 +00:00
VendorProfile res ( id ) ;
2019-04-12 14:18:46 +00:00
// Helper to get compulsory fields
2018-04-11 11:12:08 +00:00
auto get_or_throw = [ & ] ( const ptree & tree , const std : : string & key ) - > ptree : : const_assoc_iterator
{
auto res = tree . find ( key ) ;
if ( res = = tree . not_found ( ) ) {
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( ( boost : : format ( " Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`. " ) % id % key ) . str ( ) ) ;
2018-04-11 11:12:08 +00:00
}
return res ;
} ;
2019-04-12 14:18:46 +00:00
// Load the header
2018-04-11 11:12:08 +00:00
const auto & vendor_section = get_or_throw ( tree , " vendor " ) - > second ;
res . name = get_or_throw ( vendor_section , " name " ) - > second . data ( ) ;
auto config_version_str = get_or_throw ( vendor_section , " config_version " ) - > second . data ( ) ;
auto config_version = Semver : : parse ( config_version_str ) ;
if ( ! config_version ) {
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( ( boost : : format ( " Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`. " ) % id % config_version_str ) . str ( ) ) ;
2018-04-11 11:12:08 +00:00
} else {
res . config_version = std : : move ( * config_version ) ;
}
2019-04-12 14:18:46 +00:00
// Load URLs
2019-05-13 16:38:48 +00:00
const auto config_update_url = vendor_section . find ( " config_update_url " ) ;
2018-04-11 11:12:08 +00:00
if ( config_update_url ! = vendor_section . not_found ( ) ) {
res . config_update_url = config_update_url - > second . data ( ) ;
}
2019-05-13 16:38:48 +00:00
const auto changelog_url = vendor_section . find ( " changelog_url " ) ;
if ( changelog_url ! = vendor_section . not_found ( ) ) {
res . changelog_url = changelog_url - > second . data ( ) ;
}
2018-04-11 11:12:08 +00:00
if ( ! load_all ) {
return res ;
}
2019-04-12 14:18:46 +00:00
// Load printer models
2018-04-11 11:12:08 +00:00
for ( auto & section : tree ) {
if ( boost : : starts_with ( section . first , printer_model_key ) ) {
VendorProfile : : PrinterModel model ;
model . id = section . first . substr ( printer_model_key . size ( ) ) ;
model . name = section . second . get < std : : string > ( " name " , model . id ) ;
2019-01-30 14:22:17 +00:00
2019-02-07 14:55:47 +00:00
const char * technology_fallback = boost : : algorithm : : starts_with ( model . id , " SL " ) ? " SLA " : " FFF " ;
auto technology_field = section . second . get < std : : string > ( " technology " , technology_fallback ) ;
2018-07-31 13:09:57 +00:00
if ( ! ConfigOptionEnum < PrinterTechnology > : : from_string ( technology_field , model . technology ) ) {
BOOST_LOG_TRIVIAL ( error ) < < boost : : format ( " Vendor bundle: `%1%`: Invalid printer technology field: `%2%` " ) % id % technology_field ;
model . technology = ptFFF ;
}
2019-01-30 14:22:17 +00:00
2019-02-07 14:55:47 +00:00
model . family = section . second . get < std : : string > ( " family " , std : : string ( ) ) ;
2019-02-08 09:33:07 +00:00
if ( model . family . empty ( ) & & res . name = = " Prusa Research " ) {
// If no family is specified, it can be inferred for known printers
const auto from_pre_map = pre_family_model_map . find ( model . id ) ;
if ( from_pre_map ! = pre_family_model_map . end ( ) ) { model . family = from_pre_map - > second ; }
}
2018-12-21 19:09:25 +00:00
#if 0
2019-08-06 14:51:32 +00:00
// Remove SLA printers from the initial alpha.
if ( model . technology = = ptSLA )
continue ;
2018-12-21 19:09:25 +00:00
# endif
2019-08-06 14:51:32 +00:00
section . second . get < std : : string > ( " variants " , " " ) ;
2018-05-01 08:35:07 +00:00
const auto variants_field = section . second . get < std : : string > ( " variants " , " " ) ;
2018-04-11 11:12:08 +00:00
std : : vector < std : : string > variants ;
2018-05-01 08:35:07 +00:00
if ( Slic3r : : unescape_strings_cstyle ( variants_field , variants ) ) {
2018-04-11 11:12:08 +00:00
for ( const std : : string & variant_name : variants ) {
if ( model . variant ( variant_name ) = = nullptr )
model . variants . emplace_back ( VendorProfile : : PrinterVariant ( variant_name ) ) ;
}
} else {
2018-05-01 08:35:07 +00:00
BOOST_LOG_TRIVIAL ( error ) < < boost : : format ( " Vendor bundle: `%1%`: Malformed variants field: `%2%` " ) % id % variants_field ;
2018-04-11 11:12:08 +00:00
}
2020-01-13 15:01:01 +00:00
auto default_materials_field = section . second . get < std : : string > ( " default_materials " , " " ) ;
if ( default_materials_field . empty ( ) )
default_materials_field = section . second . get < std : : string > ( " default_filaments " , " " ) ;
if ( Slic3r : : unescape_strings_cstyle ( default_materials_field , model . default_materials ) ) {
Slic3r : : sort_remove_duplicates ( model . default_materials ) ;
2020-03-11 17:12:16 +00:00
if ( ! model . default_materials . empty ( ) & & model . default_materials . front ( ) . empty ( ) )
// An empty material was inserted into the list of default materials. Remove it.
model . default_materials . erase ( model . default_materials . begin ( ) ) ;
2020-01-13 15:01:01 +00:00
} else {
BOOST_LOG_TRIVIAL ( error ) < < boost : : format ( " Vendor bundle: `%1%`: Malformed default_materials field: `%2%` " ) % id % default_materials_field ;
}
2020-01-07 13:08:21 +00:00
model . bed_model = section . second . get < std : : string > ( " bed_model " , " " ) ;
model . bed_texture = section . second . get < std : : string > ( " bed_texture " , " " ) ;
2018-04-11 11:12:08 +00:00
if ( ! model . id . empty ( ) & & ! model . variants . empty ( ) )
res . models . push_back ( std : : move ( model ) ) ;
}
}
2019-04-12 14:18:46 +00:00
// Load filaments and sla materials to be installed by default
const auto filaments = tree . find ( filaments_section ) ;
if ( filaments ! = tree . not_found ( ) ) {
for ( auto & pair : filaments - > second ) {
if ( pair . second . data ( ) = = " 1 " ) {
res . default_filaments . insert ( pair . first ) ;
}
}
}
const auto materials = tree . find ( materials_section ) ;
if ( materials ! = tree . not_found ( ) ) {
for ( auto & pair : materials - > second ) {
if ( pair . second . data ( ) = = " 1 " ) {
res . default_sla_materials . insert ( pair . first ) ;
}
}
}
2018-04-11 11:12:08 +00:00
return res ;
}
2019-01-30 14:22:17 +00:00
std : : vector < std : : string > VendorProfile : : families ( ) const
{
std : : vector < std : : string > res ;
unsigned num_familiies = 0 ;
for ( auto & model : models ) {
2019-02-07 14:55:47 +00:00
if ( std : : find ( res . begin ( ) , res . end ( ) , model . family ) = = res . end ( ) ) {
2019-01-30 14:22:17 +00:00
res . push_back ( model . family ) ;
num_familiies + + ;
}
}
return res ;
}
2018-04-11 11:12:08 +00:00
2017-10-26 15:17:39 +00:00
// Suffix to be added to a modified preset name in the combo box.
2017-10-25 10:53:31 +00:00
static std : : string g_suffix_modified = " (modified) " ;
2017-10-30 17:41:50 +00:00
const std : : string & Preset : : suffix_modified ( )
{
return g_suffix_modified ;
}
2018-07-23 13:44:01 +00:00
2020-06-16 14:58:41 +00:00
void Preset : : update_suffix_modified ( const std : : string & new_suffix_modified )
2018-07-23 13:44:01 +00:00
{
2020-06-16 14:58:41 +00:00
g_suffix_modified = new_suffix_modified ;
2018-07-23 13:44:01 +00:00
}
2017-10-26 15:17:39 +00:00
// Remove an optional "(modified)" suffix from a name.
// This converts a UI name to a unique preset identifier.
2017-10-30 17:41:50 +00:00
std : : string Preset : : remove_suffix_modified ( const std : : string & name )
2017-10-26 15:17:39 +00:00
{
return boost : : algorithm : : ends_with ( name , g_suffix_modified ) ?
name . substr ( 0 , name . size ( ) - g_suffix_modified . size ( ) ) :
name ;
}
2017-10-25 10:53:31 +00:00
2017-11-01 18:30:05 +00:00
// Update new extruder fields at the printer profile.
void Preset : : normalize ( DynamicPrintConfig & config )
{
auto * nozzle_diameter = dynamic_cast < const ConfigOptionFloats * > ( config . option ( " nozzle_diameter " ) ) ;
if ( nozzle_diameter ! = nullptr )
2018-07-31 13:09:57 +00:00
// Loaded the FFF Printer settings. Verify, that all extruder dependent values have enough values.
2019-10-15 14:31:20 +00:00
config . set_num_extruders ( ( unsigned int ) nozzle_diameter - > values . size ( ) ) ;
2017-11-02 15:21:34 +00:00
if ( config . option ( " filament_diameter " ) ! = nullptr ) {
// This config contains single or multiple filament presets.
// Ensure that the filament preset vector options contain the correct number of values.
size_t n = ( nozzle_diameter = = nullptr ) ? 1 : nozzle_diameter - > values . size ( ) ;
const auto & defaults = FullPrintConfig : : defaults ( ) ;
for ( const std : : string & key : Preset : : filament_options ( ) ) {
2019-08-06 14:51:32 +00:00
if ( key = = " compatible_prints " | | key = = " compatible_printers " )
continue ;
2017-11-02 15:21:34 +00:00
auto * opt = config . option ( key , false ) ;
2018-05-23 12:15:10 +00:00
/*assert(opt != nullptr);
assert ( opt - > is_vector ( ) ) ; */
2017-11-02 15:21:34 +00:00
if ( opt ! = nullptr & & opt - > is_vector ( ) )
static_cast < ConfigOptionVectorBase * > ( opt ) - > resize ( n , defaults . option ( key ) ) ;
}
2018-05-23 11:19:25 +00:00
// The following keys are mandatory for the UI, but they are not part of FullPrintConfig, therefore they are handled separately.
for ( const std : : string & key : { " filament_settings_id " } ) {
auto * opt = config . option ( key , false ) ;
2018-11-06 18:09:54 +00:00
assert ( opt = = nullptr | | opt - > type ( ) = = coStrings ) ;
2018-05-23 11:19:25 +00:00
if ( opt ! = nullptr & & opt - > type ( ) = = coStrings )
static_cast < ConfigOptionStrings * > ( opt ) - > values . resize ( n , std : : string ( ) ) ;
}
2017-11-02 15:21:34 +00:00
}
2021-02-10 16:28:56 +00:00
if ( const auto * gap_fill_speed = config . option < ConfigOptionFloat > ( " gap_fill_speed " , false ) ; gap_fill_speed & & gap_fill_speed - > value < = 0. ) {
// Legacy conversion. If the gap fill speed is zero, it means the gap fill is not enabled.
// Set the new gap_fill_enabled value, so that it will show up in the UI as disabled.
if ( auto * gap_fill_enabled = config . option < ConfigOptionBool > ( " gap_fill_enabled " , false ) ; gap_fill_enabled )
gap_fill_enabled - > value = false ;
}
2021-04-21 10:09:36 +00:00
if ( auto * first_layer_height = config . option < ConfigOptionFloatOrPercent > ( " first_layer_height " , false ) ; first_layer_height & & first_layer_height - > percent )
if ( const auto * layer_height = config . option < ConfigOptionFloat > ( " layer_height " , false ) ; layer_height ) {
// Legacy conversion - first_layer_height moved from PrintObject setting to a Print setting, thus we are getting rid of the dependency
// of first_layer_height on PrintObject specific layer_height. Covert the first layer heigth to an absolute value.
first_layer_height - > value = first_layer_height - > get_abs_value ( layer_height - > value ) ;
first_layer_height - > percent = false ;
}
2017-10-25 10:53:31 +00:00
}
2018-10-31 15:22:36 +00:00
std : : string Preset : : remove_invalid_keys ( DynamicPrintConfig & config , const DynamicPrintConfig & default_config )
2017-09-19 11:55:48 +00:00
{
2018-10-31 15:22:36 +00:00
std : : string incorrect_keys ;
for ( const std : : string & key : config . keys ( ) )
if ( ! default_config . has ( key ) ) {
if ( incorrect_keys . empty ( ) )
incorrect_keys = key ;
else {
incorrect_keys + = " , " ;
incorrect_keys + = key ;
}
config . erase ( key ) ;
2017-11-01 18:30:05 +00:00
}
2018-10-31 15:22:36 +00:00
return incorrect_keys ;
2017-09-19 11:55:48 +00:00
}
2017-10-25 10:53:31 +00:00
void Preset : : save ( )
{
this - > config . save ( this - > file ) ;
}
// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
std : : string Preset : : label ( ) const
{
return this - > name + ( this - > is_dirty ? g_suffix_modified : " " ) ;
}
Various changes in handling of profile compatiblilities
and the "show incompatible profiles" option.
It was not able to select the incompatible Print profile, which is
possible now.
(see Cannot select incompatible printer profile #3715)
When the Printer profile derived from the Prusa3D system profile was
active or a system Prusa3D profile was active, and when the Print profile
with the removed "inherits" field was active (or any other profile
derived from the "-- default --" profile was active), then the filament
selector offered just the profiles with the removed "inherits" field
(or any other profile derived from the "-- default--") profile.
This behavior has been now changed, so that in this scenario the Filament selector
will offer the Prusa3D vendor profiles compatible with the active Print
and Printer profile as well as the user profiles.
Slicer was also changed to keep an incompatible preset selected
at its respective tab if its respective "Red flag" is enabled.
For example, if an incompatible Print preset is selected and a Printer
profile is switched to another one which is not compatible with the active
Print preset that was red already, the active Print preset is
not switched if the Print "Red flag" is active. However, if the Print profile
was compatible before the Printer profile is switched and now the Print
profile becomes incompatible, another compatible Print profile is selected.
A likely bug in wxWidgets was worked around when switching a Print preset
on Plater, if the last item in the Print preset was active and incompatible,
and another Print preset was selected by the user. On Windows, an CBN_EDITCHANGE
is sent just after combo box selection change event and the CBN_EDITCHANGE
holds an index of the combo box item, which will be removed by the 1st event,
therefore leading to an assert in wxWidgets on CBN_EDITCHANGE. The workaround
is to disable processing of CBN_EDITCHANGE on Windows for the Plater
preset selection combo boxes.
2020-02-27 10:44:01 +00:00
bool is_compatible_with_print ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_print , const PresetWithVendorProfile & active_printer )
2018-12-04 16:56:49 +00:00
{
Various changes in handling of profile compatiblilities
and the "show incompatible profiles" option.
It was not able to select the incompatible Print profile, which is
possible now.
(see Cannot select incompatible printer profile #3715)
When the Printer profile derived from the Prusa3D system profile was
active or a system Prusa3D profile was active, and when the Print profile
with the removed "inherits" field was active (or any other profile
derived from the "-- default --" profile was active), then the filament
selector offered just the profiles with the removed "inherits" field
(or any other profile derived from the "-- default--") profile.
This behavior has been now changed, so that in this scenario the Filament selector
will offer the Prusa3D vendor profiles compatible with the active Print
and Printer profile as well as the user profiles.
Slicer was also changed to keep an incompatible preset selected
at its respective tab if its respective "Red flag" is enabled.
For example, if an incompatible Print preset is selected and a Printer
profile is switched to another one which is not compatible with the active
Print preset that was red already, the active Print preset is
not switched if the Print "Red flag" is active. However, if the Print profile
was compatible before the Printer profile is switched and now the Print
profile becomes incompatible, another compatible Print profile is selected.
A likely bug in wxWidgets was worked around when switching a Print preset
on Plater, if the last item in the Print preset was active and incompatible,
and another Print preset was selected by the user. On Windows, an CBN_EDITCHANGE
is sent just after combo box selection change event and the CBN_EDITCHANGE
holds an index of the combo box item, which will be removed by the 1st event,
therefore leading to an assert in wxWidgets on CBN_EDITCHANGE. The workaround
is to disable processing of CBN_EDITCHANGE on Windows for the Plater
preset selection combo boxes.
2020-02-27 10:44:01 +00:00
if ( preset . vendor ! = nullptr & & preset . vendor ! = active_printer . vendor )
2019-12-05 13:48:11 +00:00
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false ;
auto & condition = preset . preset . compatible_prints_condition ( ) ;
auto * compatible_prints = dynamic_cast < const ConfigOptionStrings * > ( preset . preset . config . option ( " compatible_prints " ) ) ;
2018-12-04 16:56:49 +00:00
bool has_compatible_prints = compatible_prints ! = nullptr & & ! compatible_prints - > values . empty ( ) ;
if ( ! has_compatible_prints & & ! condition . empty ( ) ) {
try {
2019-12-05 13:48:11 +00:00
return PlaceholderParser : : evaluate_boolean_expression ( condition , active_print . preset . config ) ;
2018-12-04 16:56:49 +00:00
} catch ( const std : : runtime_error & err ) {
//FIXME in case of an error, return "compatible with everything".
2019-12-05 13:48:11 +00:00
printf ( " Preset::is_compatible_with_print - parsing error of compatible_prints_condition %s: \n %s \n " , active_print . preset . name . c_str ( ) , err . what ( ) ) ;
2018-12-04 16:56:49 +00:00
return true ;
}
}
2019-12-05 13:48:11 +00:00
return preset . preset . is_default | | active_print . preset . name . empty ( ) | | ! has_compatible_prints | |
std : : find ( compatible_prints - > values . begin ( ) , compatible_prints - > values . end ( ) , active_print . preset . name ) ! =
2018-12-04 16:56:49 +00:00
compatible_prints - > values . end ( ) ;
}
2019-12-05 13:48:11 +00:00
bool is_compatible_with_printer ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_printer , const DynamicPrintConfig * extra_config )
2017-09-19 11:55:48 +00:00
{
2019-12-05 13:48:11 +00:00
if ( preset . vendor ! = nullptr & & preset . vendor ! = active_printer . vendor )
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false ;
auto & condition = preset . preset . compatible_printers_condition ( ) ;
auto * compatible_printers = dynamic_cast < const ConfigOptionStrings * > ( preset . preset . config . option ( " compatible_printers " ) ) ;
2017-12-20 12:32:02 +00:00
bool has_compatible_printers = compatible_printers ! = nullptr & & ! compatible_printers - > values . empty ( ) ;
2018-06-27 14:57:42 +00:00
if ( ! has_compatible_printers & & ! condition . empty ( ) ) {
2017-12-18 14:07:38 +00:00
try {
2019-12-05 13:48:11 +00:00
return PlaceholderParser : : evaluate_boolean_expression ( condition , active_printer . preset . config , extra_config ) ;
2017-12-18 14:07:38 +00:00
} catch ( const std : : runtime_error & err ) {
//FIXME in case of an error, return "compatible with everything".
2019-12-05 13:48:11 +00:00
printf ( " Preset::is_compatible_with_printer - parsing error of compatible_printers_condition %s: \n %s \n " , active_printer . preset . name . c_str ( ) , err . what ( ) ) ;
2017-12-18 14:07:38 +00:00
return true ;
}
}
2019-12-05 13:48:11 +00:00
return preset . preset . is_default | | active_printer . preset . name . empty ( ) | | ! has_compatible_printers | |
std : : find ( compatible_printers - > values . begin ( ) , compatible_printers - > values . end ( ) , active_printer . preset . name ) ! =
2017-09-19 11:55:48 +00:00
compatible_printers - > values . end ( ) ;
2017-11-10 16:27:05 +00:00
}
2019-12-05 13:48:11 +00:00
bool is_compatible_with_printer ( const PresetWithVendorProfile & preset , const PresetWithVendorProfile & active_printer )
2017-12-19 15:48:14 +00:00
{
DynamicPrintConfig config ;
2019-12-05 13:48:11 +00:00
config . set_key_value ( " printer_preset " , new ConfigOptionString ( active_printer . preset . name ) ) ;
const ConfigOption * opt = active_printer . preset . config . option ( " nozzle_diameter " ) ;
2019-08-06 14:51:32 +00:00
if ( opt )
config . set_key_value ( " num_extruders " , new ConfigOptionInt ( ( int ) static_cast < const ConfigOptionFloats * > ( opt ) - > values . size ( ) ) ) ;
2019-12-05 13:48:11 +00:00
return is_compatible_with_printer ( preset , active_printer , & config ) ;
2017-09-19 11:55:48 +00:00
}
2018-03-29 15:54:43 +00:00
void Preset : : set_visible_from_appconfig ( const AppConfig & app_config )
{
if ( vendor = = nullptr ) { return ; }
2019-04-12 14:18:46 +00:00
if ( type = = TYPE_PRINTER ) {
const std : : string & model = config . opt_string ( " printer_model " ) ;
const std : : string & variant = config . opt_string ( " printer_variant " ) ;
2020-03-02 15:15:41 +00:00
if ( model . empty ( ) | | variant . empty ( ) )
return ;
2019-04-12 14:18:46 +00:00
is_visible = app_config . get_variant ( vendor - > id , model , variant ) ;
2020-03-02 15:15:41 +00:00
} else if ( type = = TYPE_FILAMENT | | type = = TYPE_SLA_MATERIAL ) {
2020-03-14 17:35:42 +00:00
const std : : string & section_name = ( type = = TYPE_FILAMENT ) ? AppConfig : : SECTION_FILAMENTS : AppConfig : : SECTION_MATERIALS ;
2020-03-02 15:15:41 +00:00
if ( app_config . has_section ( section_name ) ) {
2021-02-24 08:22:31 +00:00
// Check whether this profile is marked as "installed" in PrusaSlicer.ini,
2020-03-02 15:15:41 +00:00
// or whether a profile is marked as "installed", which this profile may have been renamed from.
const std : : map < std : : string , std : : string > & installed = app_config . get_section ( section_name ) ;
auto has = [ & installed ] ( const std : : string & name ) {
auto it = installed . find ( name ) ;
return it ! = installed . end ( ) & & ! it - > second . empty ( ) ;
} ;
is_visible = has ( this - > name ) ;
for ( auto it = this - > renamed_from . begin ( ) ; ! is_visible & & it ! = this - > renamed_from . end ( ) ; + + it )
is_visible = has ( * it ) ;
}
2019-04-12 14:18:46 +00:00
}
2018-03-29 15:54:43 +00:00
}
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_Preset_print_options {
" layer_height " , " first_layer_height " , " perimeters " , " spiral_vase " , " slice_closing_radius " , " slicing_mode " ,
" top_solid_layers " , " top_solid_min_thickness " , " bottom_solid_layers " , " bottom_solid_min_thickness " ,
" extra_perimeters " , " ensure_vertical_shell_thickness " , " avoid_crossing_perimeters " , " thin_walls " , " overhangs " ,
" seam_position " , " external_perimeters_first " , " fill_density " , " fill_pattern " , " top_fill_pattern " , " bottom_fill_pattern " ,
" infill_every_layers " , " infill_only_where_needed " , " solid_infill_every_layers " , " fill_angle " , " bridge_angle " ,
" solid_infill_below_area " , " only_retract_when_crossing_perimeters " , " infill_first " ,
" ironing " , " ironing_type " , " ironing_flowrate " , " ironing_speed " , " ironing_spacing " ,
" max_print_speed " , " max_volumetric_speed " , " avoid_crossing_perimeters_max_detour " ,
" fuzzy_skin " , " fuzzy_skin_thickness " , " fuzzy_skin_point_dist " ,
2019-01-29 17:07:45 +00:00
# ifdef HAS_PRESSURE_EQUALIZER
2021-08-24 11:10:35 +00:00
" max_volumetric_extrusion_rate_slope_positive " , " max_volumetric_extrusion_rate_slope_negative " ,
2019-01-29 17:07:45 +00:00
# endif /* HAS_PRESSURE_EQUALIZER */
2021-08-24 11:10:35 +00:00
" perimeter_speed " , " small_perimeter_speed " , " external_perimeter_speed " , " infill_speed " , " solid_infill_speed " ,
" top_solid_infill_speed " , " support_material_speed " , " support_material_xy_spacing " , " support_material_interface_speed " ,
2021-08-31 14:03:07 +00:00
" bridge_speed " , " gap_fill_speed " , " gap_fill_enabled " , " travel_speed " , " travel_speed_z " , " first_layer_speed " , " first_layer_speed_over_raft " , " perimeter_acceleration " , " infill_acceleration " ,
" bridge_acceleration " , " first_layer_acceleration " , " first_layer_acceleration_over_raft " , " default_acceleration " , " skirts " , " skirt_distance " , " skirt_height " , " draft_shield " ,
2021-08-31 10:22:38 +00:00
" min_skirt_length " , " brim_width " , " brim_separation " , " brim_type " , " support_material " , " support_material_auto " , " support_material_threshold " , " support_material_enforce_layers " ,
2021-08-24 11:10:35 +00:00
" raft_layers " , " raft_first_layer_density " , " raft_first_layer_expansion " , " raft_contact_distance " , " raft_expansion " ,
" support_material_pattern " , " support_material_with_sheath " , " support_material_spacing " , " support_material_closing_radius " , " support_material_style " ,
" support_material_synchronize_layers " , " support_material_angle " , " support_material_interface_layers " , " support_material_bottom_interface_layers " ,
" support_material_interface_pattern " , " support_material_interface_spacing " , " support_material_interface_contact_loops " ,
" support_material_contact_distance " , " support_material_bottom_contact_distance " ,
" support_material_buildplate_only " , " dont_support_bridges " , " thick_bridges " , " notes " , " complete_objects " , " extruder_clearance_radius " ,
" extruder_clearance_height " , " gcode_comments " , " gcode_label_objects " , " output_filename_format " , " post_process " , " perimeter_extruder " ,
" infill_extruder " , " solid_infill_extruder " , " support_material_extruder " , " support_material_interface_extruder " ,
" ooze_prevention " , " standby_temperature_delta " , " interface_shells " , " extrusion_width " , " first_layer_extrusion_width " ,
" perimeter_extrusion_width " , " external_perimeter_extrusion_width " , " infill_extrusion_width " , " solid_infill_extrusion_width " ,
" top_infill_extrusion_width " , " support_material_extrusion_width " , " infill_overlap " , " infill_anchor " , " infill_anchor_max " , " bridge_flow_ratio " , " clip_multipart_objects " ,
" elefant_foot_compensation " , " xy_size_compensation " , " threads " , " resolution " , " wipe_tower " , " wipe_tower_x " , " wipe_tower_y " ,
" wipe_tower_width " , " wipe_tower_rotation_angle " , " wipe_tower_brim_width " , " wipe_tower_bridging " , " single_extruder_multi_material_priming " , " mmu_segmented_region_max_width " ,
" wipe_tower_no_sparse_layers " , " compatible_printers " , " compatible_printers_condition " , " inherits "
} ;
2017-11-01 18:30:05 +00:00
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_Preset_filament_options {
" filament_colour " , " filament_diameter " , " filament_type " , " filament_soluble " , " filament_notes " , " filament_max_volumetric_speed " ,
" extrusion_multiplier " , " filament_density " , " filament_cost " , " filament_spool_weight " , " filament_loading_speed " , " filament_loading_speed_start " , " filament_load_time " ,
" filament_unloading_speed " , " filament_unloading_speed_start " , " filament_unload_time " , " filament_toolchange_delay " , " filament_cooling_moves " ,
" filament_cooling_initial_speed " , " filament_cooling_final_speed " , " filament_ramming_parameters " , " filament_minimal_purge_on_wipe_tower " ,
" temperature " , " first_layer_temperature " , " bed_temperature " , " first_layer_bed_temperature " , " fan_always_on " , " cooling " , " min_fan_speed " ,
" max_fan_speed " , " bridge_fan_speed " , " disable_fan_first_layers " , " full_fan_speed_layer " , " fan_below_layer_time " , " slowdown_below_layer_time " , " min_print_speed " ,
" start_filament_gcode " , " end_filament_gcode " ,
// Retract overrides
" filament_retract_length " , " filament_retract_lift " , " filament_retract_lift_above " , " filament_retract_lift_below " , " filament_retract_speed " , " filament_deretract_speed " , " filament_retract_restart_extra " , " filament_retract_before_travel " ,
" filament_retract_layer_change " , " filament_wipe " , " filament_retract_before_wipe " ,
// Profile compatibility
" filament_vendor " , " compatible_prints " , " compatible_prints_condition " , " compatible_printers " , " compatible_printers_condition " , " inherits "
} ;
2017-11-01 18:30:05 +00:00
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_Preset_machine_limits_options {
" machine_max_acceleration_extruding " , " machine_max_acceleration_retracting " , " machine_max_acceleration_travel " ,
" machine_max_acceleration_x " , " machine_max_acceleration_y " , " machine_max_acceleration_z " , " machine_max_acceleration_e " ,
" machine_max_feedrate_x " , " machine_max_feedrate_y " , " machine_max_feedrate_z " , " machine_max_feedrate_e " ,
" machine_min_extruding_rate " , " machine_min_travel_rate " ,
" machine_max_jerk_x " , " machine_max_jerk_y " , " machine_max_jerk_z " , " machine_max_jerk_e " ,
} ;
2020-10-02 15:31:55 +00:00
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_Preset_printer_options {
" printer_technology " ,
" bed_shape " , " bed_custom_texture " , " bed_custom_model " , " z_offset " , " gcode_flavor " , " use_relative_e_distances " ,
" use_firmware_retraction " , " use_volumetric_e " , " variable_layer_height " ,
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
" host_type " , " print_host " , " printhost_apikey " , " printhost_cafile " ,
" single_extruder_multi_material " , " start_gcode " , " end_gcode " , " before_layer_gcode " , " layer_gcode " , " toolchange_gcode " ,
" color_change_gcode " , " pause_print_gcode " , " template_custom_gcode " ,
" between_objects_gcode " , " printer_vendor " , " printer_model " , " printer_variant " , " printer_notes " , " cooling_tube_retraction " ,
" cooling_tube_length " , " high_current_on_filament_swap " , " parking_pos_retraction " , " extra_loading_move " , " max_print_height " ,
" default_print_profile " , " inherits " ,
" remaining_times " , " silent_mode " ,
" machine_limits_usage " , " thumbnails "
} ;
2017-11-01 18:30:05 +00:00
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_Preset_sla_print_options {
" layer_height " ,
" faded_layers " ,
" supports_enable " ,
" support_head_front_diameter " ,
" support_head_penetration " ,
" support_head_width " ,
" support_pillar_diameter " ,
" support_small_pillar_diameter_percent " ,
" support_max_bridges_on_pillar " ,
" support_pillar_connection_mode " ,
" support_buildplate_only " ,
" support_pillar_widening_factor " ,
" support_base_diameter " ,
" support_base_height " ,
" support_base_safety_distance " ,
" support_critical_angle " ,
" support_max_bridge_length " ,
" support_max_pillar_link_distance " ,
" support_object_elevation " ,
" support_points_density_relative " ,
" support_points_minimal_distance " ,
" slice_closing_radius " ,
" slicing_mode " ,
" pad_enable " ,
" pad_wall_thickness " ,
" pad_wall_height " ,
" pad_brim_size " ,
" pad_max_merge_distance " ,
// "pad_edge_radius",
" pad_wall_slope " ,
" pad_object_gap " ,
" pad_around_object " ,
" pad_around_object_everywhere " ,
" pad_object_connector_stride " ,
" pad_object_connector_width " ,
" pad_object_connector_penetration " ,
" hollowing_enable " ,
" hollowing_min_thickness " ,
" hollowing_quality " ,
" hollowing_closing_distance " ,
" output_filename_format " ,
" default_sla_print_profile " ,
" compatible_printers " ,
" compatible_printers_condition " ,
" inherits "
} ;
2017-11-01 18:30:05 +00:00
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_Preset_sla_material_options {
2021-11-05 16:19:57 +00:00
" material_colour " ,
2021-08-24 11:10:35 +00:00
" material_type " ,
" initial_layer_height " ,
" bottle_cost " ,
" bottle_volume " ,
" bottle_weight " ,
" material_density " ,
" exposure_time " ,
" initial_exposure_time " ,
" material_correction " ,
" material_notes " ,
" material_vendor " ,
" default_sla_material_profile " ,
" compatible_prints " , " compatible_prints_condition " ,
" compatible_printers " , " compatible_printers_condition " , " inherits "
} ;
2018-07-31 13:09:57 +00:00
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_Preset_sla_printer_options {
" printer_technology " ,
" bed_shape " , " bed_custom_texture " , " bed_custom_model " , " max_print_height " ,
" display_width " , " display_height " , " display_pixels_x " , " display_pixels_y " ,
" display_mirror_x " , " display_mirror_y " ,
" display_orientation " ,
" fast_tilt_time " , " slow_tilt_time " , " area_fill " ,
" relative_correction " ,
" absolute_correction " ,
" elefant_foot_compensation " ,
" elefant_foot_min_width " ,
" gamma_correction " ,
" min_exposure_time " , " max_exposure_time " ,
" min_initial_exposure_time " , " max_initial_exposure_time " ,
//FIXME the print host keys are left here just for conversion from the Printer preset to Physical Printer preset.
" print_host " , " printhost_apikey " , " printhost_cafile " ,
" printer_notes " ,
" inherits "
} ;
2018-07-31 13:09:57 +00:00
2021-08-24 11:10:35 +00:00
const std : : vector < std : : string > & Preset : : print_options ( ) { return s_Preset_print_options ; }
const std : : vector < std : : string > & Preset : : filament_options ( ) { return s_Preset_filament_options ; }
const std : : vector < std : : string > & Preset : : machine_limits_options ( ) { return s_Preset_machine_limits_options ; }
// The following nozzle options of a printer profile will be adjusted to match the size
// of the nozzle_diameter vector.
const std : : vector < std : : string > & Preset : : nozzle_options ( ) { return print_config_def . extruder_option_keys ( ) ; }
const std : : vector < std : : string > & Preset : : sla_print_options ( ) { return s_Preset_sla_print_options ; }
const std : : vector < std : : string > & Preset : : sla_material_options ( ) { return s_Preset_sla_material_options ; }
const std : : vector < std : : string > & Preset : : sla_printer_options ( ) { return s_Preset_sla_printer_options ; }
const std : : vector < std : : string > & Preset : : printer_options ( )
2019-08-06 14:51:32 +00:00
{
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_opts = [ ] ( ) {
std : : vector < std : : string > opts = s_Preset_printer_options ;
append ( opts , s_Preset_machine_limits_options ) ;
append ( opts , Preset : : nozzle_options ( ) ) ;
return opts ;
} ( ) ;
2018-11-16 16:36:23 +00:00
return s_opts ;
}
2018-08-03 12:14:25 +00:00
PresetCollection : : PresetCollection ( Preset : : Type type , const std : : vector < std : : string > & keys , const Slic3r : : StaticPrintConfig & defaults , const std : : string & default_name ) :
2017-09-19 11:55:48 +00:00
m_type ( type ) ,
2017-10-25 10:53:31 +00:00
m_edited_preset ( type , " " , false ) ,
2021-04-06 14:29:05 +00:00
m_saved_preset ( type , " " , false ) ,
2020-06-16 10:57:49 +00:00
m_idx_selected ( 0 )
2017-09-19 11:55:48 +00:00
{
// Insert just the default preset.
2018-08-03 12:14:25 +00:00
this - > add_default_preset ( keys , defaults , default_name ) ;
2017-10-25 10:53:31 +00:00
m_edited_preset . config . apply ( m_presets . front ( ) . config ) ;
2021-04-06 14:29:05 +00:00
update_saved_preset_from_current_preset ( ) ;
2017-10-25 10:53:31 +00:00
}
2017-12-19 18:51:22 +00:00
void PresetCollection : : reset ( bool delete_files )
{
2018-08-01 09:09:51 +00:00
if ( m_presets . size ( ) > m_num_default_presets ) {
2017-12-19 18:51:22 +00:00
if ( delete_files ) {
// Erase the preset files.
for ( Preset & preset : m_presets )
2018-03-09 15:37:33 +00:00
if ( ! preset . is_default & & ! preset . is_external & & ! preset . is_system )
2017-12-19 18:51:22 +00:00
boost : : nowide : : remove ( preset . file . c_str ( ) ) ;
}
// Don't use m_presets.resize() here as it requires a default constructor for Preset.
2018-08-01 09:09:51 +00:00
m_presets . erase ( m_presets . begin ( ) + m_num_default_presets , m_presets . end ( ) ) ;
2017-12-19 18:51:22 +00:00
this - > select_preset ( 0 ) ;
}
2020-01-23 15:04:17 +00:00
m_map_alias_to_profile_name . clear ( ) ;
2020-01-02 15:40:32 +00:00
m_map_system_profile_renamed . clear ( ) ;
2017-12-19 18:51:22 +00:00
}
2018-08-03 12:14:25 +00:00
void PresetCollection : : add_default_preset ( const std : : vector < std : : string > & keys , const Slic3r : : StaticPrintConfig & defaults , const std : : string & preset_name )
2018-08-01 09:09:51 +00:00
{
// Insert just the default preset.
m_presets . emplace_back ( Preset ( this - > type ( ) , preset_name , true ) ) ;
2019-08-06 14:51:32 +00:00
m_presets . back ( ) . config . apply_only ( defaults , keys . empty ( ) ? defaults . keys ( ) : keys ) ;
m_presets . back ( ) . loaded = true ;
2018-08-01 09:09:51 +00:00
+ + m_num_default_presets ;
}
2017-10-02 15:35:00 +00:00
// Load all presets found in dir_path.
// Throws an exception on error.
Support for forward compatibility of configurations, user and system
config bundles, project files (3MFs, AMFs). When loading these files,
the caller may decide whether to substitute some of the configuration
values the current PrusaSlicer version does not understand with
some reasonable default value, and whether to report it. If substitution
is disabled, an exception is being thrown as before this commit.
If substitution is enabled, list of substitutions is returned by the
API to be presented to the user. This allows us to introduce for example
new firmware flavor key in PrusaSlicer 2.4 while letting PrusaSlicer
2.3.2 to fall back to some default and to report it to the user.
When slicing from command line, substutions are performed by default
and reported into the console, however substitutions may be either
disabled or made silent with the new "config-compatibility" command
line option.
Substitute enums and bools only. Allow booleans to be parsed as
true: "1", "enabled", "on" case insensitive
false: "0", "disabled", "off" case insensitive
This will allow us in the future for example to switch the draft_shield
boolean to an enum with the following values: "disabled" / "enabled" / "limited".
Added "enum_bitmask.hpp" - support for type safe sets of options.
See for example PresetBundle::load_configbundle(...
LoadConfigBundleAttributes flags) for an example of intended usage.
WIP: GUI for reporting the list of config substitutions needs to be
implemented by @YuSanka.
2021-06-27 14:04:23 +00:00
void PresetCollection : : load_presets (
const std : : string & dir_path , const std : : string & subdir ,
PresetsConfigSubstitutions & substitutions , ForwardCompatibilitySubstitutionRule substitution_rule )
2017-10-02 15:35:00 +00:00
{
2021-02-24 08:22:31 +00:00
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
2020-09-01 12:15:19 +00:00
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost : : filesystem : : path dir = boost : : filesystem : : absolute ( boost : : filesystem : : path ( dir_path ) / subdir ) . make_preferred ( ) ;
2019-08-06 14:51:32 +00:00
m_dir_path = dir . string ( ) ;
2017-11-28 09:08:01 +00:00
std : : string errors_cummulative ;
2019-08-06 14:51:32 +00:00
// Store the loaded presets into a new vector, otherwise the binary search for already existing presets would be broken.
// (see the "Preset already present, not loading" message).
std : : deque < Preset > presets_loaded ;
for ( auto & dir_entry : boost : : filesystem : : directory_iterator ( dir ) )
2019-02-03 14:30:37 +00:00
if ( Slic3r : : is_ini_file ( dir_entry ) ) {
2017-11-02 15:40:25 +00:00
std : : string name = dir_entry . path ( ) . filename ( ) . string ( ) ;
2017-10-02 15:35:00 +00:00
// Remove the .ini suffix.
name . erase ( name . size ( ) - 4 ) ;
2018-03-09 15:37:33 +00:00
if ( this - > find_preset ( name , false ) ) {
2018-05-01 08:35:07 +00:00
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
BOOST_LOG_TRIVIAL ( warning ) < < " Preset already present, not loading: " < < name ;
2018-03-09 15:37:33 +00:00
continue ;
}
2017-10-02 15:35:00 +00:00
try {
Preset preset ( m_type , name , false ) ;
2017-11-02 15:40:25 +00:00
preset . file = dir_entry . path ( ) . string ( ) ;
2018-10-31 15:22:36 +00:00
// Load the preset file, apply preset values on top of defaults.
try {
DynamicPrintConfig config ;
Support for forward compatibility of configurations, user and system
config bundles, project files (3MFs, AMFs). When loading these files,
the caller may decide whether to substitute some of the configuration
values the current PrusaSlicer version does not understand with
some reasonable default value, and whether to report it. If substitution
is disabled, an exception is being thrown as before this commit.
If substitution is enabled, list of substitutions is returned by the
API to be presented to the user. This allows us to introduce for example
new firmware flavor key in PrusaSlicer 2.4 while letting PrusaSlicer
2.3.2 to fall back to some default and to report it to the user.
When slicing from command line, substutions are performed by default
and reported into the console, however substitutions may be either
disabled or made silent with the new "config-compatibility" command
line option.
Substitute enums and bools only. Allow booleans to be parsed as
true: "1", "enabled", "on" case insensitive
false: "0", "disabled", "off" case insensitive
This will allow us in the future for example to switch the draft_shield
boolean to an enum with the following values: "disabled" / "enabled" / "limited".
Added "enum_bitmask.hpp" - support for type safe sets of options.
See for example PresetBundle::load_configbundle(...
LoadConfigBundleAttributes flags) for an example of intended usage.
WIP: GUI for reporting the list of config substitutions needs to be
implemented by @YuSanka.
2021-06-27 14:04:23 +00:00
ConfigSubstitutions config_substitutions = config . load_from_ini ( preset . file , substitution_rule ) ;
if ( ! config_substitutions . empty ( ) )
substitutions . push_back ( { preset . name , m_type , PresetConfigSubstitutions : : Source : : UserFile , preset . file , std : : move ( config_substitutions ) } ) ;
2018-10-31 15:22:36 +00:00
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
2019-08-06 14:51:32 +00:00
const Preset & default_preset = this - > default_preset_for ( config ) ;
2018-10-31 15:22:36 +00:00
preset . config = default_preset . config ;
preset . config . apply ( std : : move ( config ) ) ;
Preset : : normalize ( preset . config ) ;
// Report configuration fields, which are misplaced into a wrong group.
2019-08-06 14:51:32 +00:00
std : : string incorrect_keys = Preset : : remove_invalid_keys ( config , default_preset . config ) ;
2018-10-31 15:22:36 +00:00
if ( ! incorrect_keys . empty ( ) )
BOOST_LOG_TRIVIAL ( error ) < < " Error in a preset file: The preset \" " < <
preset . file < < " \" contains the following incorrect keys: " < < incorrect_keys < < " , which were removed " ;
preset . loaded = true ;
} catch ( const std : : ifstream : : failure & err ) {
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( std : : string ( " The selected preset cannot be loaded: " ) + preset . file + " \n \t Reason: " + err . what ( ) ) ;
2018-10-31 15:22:36 +00:00
} catch ( const std : : runtime_error & err ) {
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( std : : string ( " Failed loading the preset file: " ) + preset . file + " \n \t Reason: " + err . what ( ) ) ;
2018-10-31 15:22:36 +00:00
}
2019-08-06 14:51:32 +00:00
presets_loaded . emplace_back ( preset ) ;
2017-11-28 09:08:01 +00:00
} catch ( const std : : runtime_error & err ) {
errors_cummulative + = err . what ( ) ;
errors_cummulative + = " \n " ;
2019-08-06 14:51:32 +00:00
}
2017-10-02 15:35:00 +00:00
}
2019-08-06 14:51:32 +00:00
m_presets . insert ( m_presets . end ( ) , std : : make_move_iterator ( presets_loaded . begin ( ) ) , std : : make_move_iterator ( presets_loaded . end ( ) ) ) ;
2018-08-01 09:09:51 +00:00
std : : sort ( m_presets . begin ( ) + m_num_default_presets , m_presets . end ( ) ) ;
2017-10-26 15:17:39 +00:00
this - > select_preset ( first_visible_idx ( ) ) ;
2017-11-28 09:08:01 +00:00
if ( ! errors_cummulative . empty ( ) )
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( errors_cummulative ) ;
2017-10-25 10:53:31 +00:00
}
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
Preset & PresetCollection : : load_preset ( const std : : string & path , const std : : string & name , const DynamicPrintConfig & config , bool select )
2017-11-02 15:21:34 +00:00
{
DynamicPrintConfig cfg ( this - > default_preset ( ) . config ) ;
2017-11-03 18:14:33 +00:00
cfg . apply_only ( config , cfg . keys ( ) , true ) ;
2018-06-22 12:17:03 +00:00
return this - > load_preset ( path , name , std : : move ( cfg ) , select ) ;
}
2020-02-18 13:13:48 +00:00
static bool profile_print_params_same ( const DynamicPrintConfig & cfg_old , const DynamicPrintConfig & cfg_new )
{
t_config_option_keys diff = cfg_old . diff ( cfg_new ) ;
2018-06-26 09:31:01 +00:00
// Following keys are used by the UI, not by the slicing core, therefore they are not important
// when comparing profiles for equality. Ignore them.
2019-08-06 14:51:32 +00:00
for ( const char * key : { " compatible_prints " , " compatible_prints_condition " ,
" compatible_printers " , " compatible_printers_condition " , " inherits " ,
2018-12-03 12:14:28 +00:00
" print_settings_id " , " filament_settings_id " , " sla_print_settings_id " , " sla_material_settings_id " , " printer_settings_id " ,
2018-12-06 13:42:15 +00:00
" printer_model " , " printer_variant " , " default_print_profile " , " default_filament_profile " , " default_sla_print_profile " , " default_sla_material_profile " ,
2020-10-28 08:51:05 +00:00
//FIXME remove the print host keys?
2020-02-18 13:13:48 +00:00
" print_host " , " printhost_apikey " , " printhost_cafile " } )
2018-06-26 09:31:01 +00:00
diff . erase ( std : : remove ( diff . begin ( ) , diff . end ( ) , key ) , diff . end ( ) ) ;
// Preset with the same name as stored inside the config exists.
2020-10-28 08:51:05 +00:00
return diff . empty ( ) ;
2018-06-26 09:31:01 +00:00
}
2018-06-22 12:17:03 +00:00
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
// Only a single profile could be edited at at the same time, which introduces complexity when loading
// filament profiles for multi-extruder printers.
std : : pair < Preset * , bool > PresetCollection : : load_external_preset (
2018-06-22 12:17:03 +00:00
// Path to the profile source file (a G-code, an AMF or 3MF file, a config file)
const std : : string & path ,
// Name of the profile, derived from the source file name.
const std : : string & name ,
// Original name of the profile, extracted from the loaded config. Empty, if the name has not been stored.
const std : : string & original_name ,
2020-12-05 07:14:04 +00:00
// Config to initialize the preset from. It may contain configs of all presets merged in a single dictionary!
const DynamicPrintConfig & combined_config ,
2018-06-22 12:17:03 +00:00
// Select the preset after loading?
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
LoadAndSelect select )
2018-06-22 12:17:03 +00:00
{
// Load the preset over a default preset, so that the missing fields are filled in from the default preset.
2020-12-05 07:14:04 +00:00
DynamicPrintConfig cfg ( this - > default_preset_for ( combined_config ) . config ) ;
const auto & keys = cfg . keys ( ) ;
cfg . apply_only ( combined_config , keys , true ) ;
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
std : : string & inherits = Preset : : inherits ( cfg ) ;
if ( select = = LoadAndSelect : : Never ) {
// Some filament profile has been selected and modified already.
// Check whether this profile is equal to the modified edited profile.
const Preset & edited = this - > get_edited_preset ( ) ;
if ( ( edited . name = = original_name | | edited . name = = inherits ) & & profile_print_params_same ( edited . config , cfg ) )
// Just point to that already selected and edited profile.
return std : : make_pair ( & ( * this - > find_preset_internal ( edited . name ) ) , false ) ;
}
2018-06-22 12:17:03 +00:00
// Is there a preset already loaded with the name stored inside the config?
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
std : : deque < Preset > : : iterator it = this - > find_preset_internal ( original_name ) ;
bool found = it ! = m_presets . end ( ) & & it - > name = = original_name ;
2020-01-02 15:40:32 +00:00
if ( ! found ) {
// Try to match the original_name against the "renamed_from" profile names of loaded system profiles.
it = this - > find_preset_renamed ( original_name ) ;
found = it ! = m_presets . end ( ) ;
}
2020-10-28 08:51:05 +00:00
if ( found & & profile_print_params_same ( it - > config , cfg ) ) {
// The preset exists and it matches the values stored inside config.
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
if ( select = = LoadAndSelect : : Always )
2020-10-28 08:51:05 +00:00
this - > select_preset ( it - m_presets . begin ( ) ) ;
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
return std : : make_pair ( & ( * it ) , false ) ;
}
if ( ! found & & select ! = LoadAndSelect : : Never & & ! inherits . empty ( ) ) {
// Try to use a system profile as a base to select the system profile
// and override its settings with the loaded ones.
assert ( it = = m_presets . end ( ) ) ;
it = this - > find_preset_internal ( inherits ) ;
found = it ! = m_presets . end ( ) & & it - > name = = inherits ;
if ( found & & profile_print_params_same ( it - > config , cfg ) ) {
// The system preset exists and it matches the values stored inside config.
if ( select = = LoadAndSelect : : Always )
this - > select_preset ( it - m_presets . begin ( ) ) ;
return std : : make_pair ( & ( * it ) , false ) ;
}
2018-06-26 09:31:01 +00:00
}
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
if ( found ) {
if ( select ! = LoadAndSelect : : Never ) {
// Select the existing preset and override it with new values, so that
// the differences will be shown in the preset editor against the referenced profile.
this - > select_preset ( it - m_presets . begin ( ) ) ;
2020-12-05 07:14:04 +00:00
// The source config may contain keys from many possible preset types. Just copy those that relate to this preset.
this - > get_edited_preset ( ) . config . apply_only ( combined_config , keys , true ) ;
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
this - > update_dirty ( ) ;
2021-04-06 14:29:05 +00:00
update_saved_preset_from_current_preset ( ) ;
2021-07-30 07:33:51 +00:00
assert ( this - > get_edited_preset ( ) . is_dirty ) ;
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
return std : : make_pair ( & ( * it ) , this - > get_edited_preset ( ) . is_dirty ) ;
}
if ( inherits . empty ( ) ) {
// Update the "inherits" field.
// There is a profile with the same name already loaded. Should we update the "inherits" field?
inherits = it - > vendor ? it - > name : it - > inherits ( ) ;
}
2018-06-27 14:57:42 +00:00
}
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
2018-06-26 09:31:01 +00:00
// The external preset does not match an internal preset, load the external preset.
std : : string new_name ;
for ( size_t idx = 0 ; ; + + idx ) {
std : : string suffix ;
if ( original_name . empty ( ) ) {
if ( idx > 0 )
suffix = " ( " + std : : to_string ( idx ) + " ) " ;
} else {
if ( idx = = 0 )
2019-08-06 14:51:32 +00:00
suffix = " ( " + original_name + " ) " ;
2018-06-26 09:31:01 +00:00
else
suffix = " ( " + original_name + " - " + std : : to_string ( idx ) + " ) " ;
}
new_name = name + suffix ;
it = this - > find_preset_internal ( new_name ) ;
2018-06-28 18:13:01 +00:00
if ( it = = m_presets . end ( ) | | it - > name ! = new_name )
2018-06-26 09:31:01 +00:00
// Unique profile name. Insert a new profile.
break ;
if ( profile_print_params_same ( it - > config , cfg ) ) {
2018-06-22 12:17:03 +00:00
// The preset exists and it matches the values stored inside config.
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
if ( select = = LoadAndSelect : : Always )
2018-06-22 12:17:03 +00:00
this - > select_preset ( it - m_presets . begin ( ) ) ;
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
return std : : make_pair ( & ( * it ) , false ) ;
2018-06-22 12:17:03 +00:00
}
2018-06-26 09:31:01 +00:00
// Form another profile name.
2018-06-22 12:17:03 +00:00
}
2018-06-26 09:31:01 +00:00
// Insert a new profile.
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
Preset & preset = this - > load_preset ( path , new_name , std : : move ( cfg ) , select = = LoadAndSelect : : Always ) ;
2018-06-22 12:17:03 +00:00
preset . is_external = true ;
2018-07-17 08:41:17 +00:00
if ( & this - > get_selected_preset ( ) = = & preset )
this - > get_edited_preset ( ) . is_external = true ;
1) Storing the physical_printer_settings_id into the 3MF, AMF, GCode.
2) Activating the physical_printer_settings_id when loading from 3MF, AMF, GCode.
The physical printer is only activated if it references the printer_settings_id
loaded from the same file.
3) When loading the presets from 3MF, AMF, GCode, the "external" profiles
are no more created for profiles which differ from the local profiles
the loaded profiles reference. Instead, the referenced profile is activated
and modified with the loaded preset. If the referenced profile does not
exist, but the profile refers to a system profile with the "inherits"
fileds, the system profile is loaded and modified instead.
This works for all profiles with the exception of multi-extruder
printer with multiple filament profiles modified. In that case
the first modified filament profile will be loaded as modified,
while the other modified profiles will be loaded as "external".
This should fix
Physical printer + 3mf file, wrong preset used to generate gcode #5178
and possibly
https://github.com/prusa3d/PrusaSlicer/issues/5272
2020-12-04 09:48:44 +00:00
return std : : make_pair ( & preset , false ) ;
2017-11-02 15:21:34 +00:00
}
Preset & PresetCollection : : load_preset ( const std : : string & path , const std : : string & name , DynamicPrintConfig & & config , bool select )
2017-10-25 10:53:31 +00:00
{
2017-12-06 15:47:53 +00:00
auto it = this - > find_preset_internal ( name ) ;
if ( it = = m_presets . end ( ) | | it - > name ! = name ) {
// The preset was not found. Create a new preset.
2017-10-25 10:53:31 +00:00
it = m_presets . emplace ( it , Preset ( m_type , name , false ) ) ;
2017-12-06 15:47:53 +00:00
}
2017-10-25 10:53:31 +00:00
Preset & preset = * it ;
preset . file = path ;
2017-11-02 15:21:34 +00:00
preset . config = std : : move ( config ) ;
2017-10-25 10:53:31 +00:00
preset . loaded = true ;
2017-11-02 15:21:34 +00:00
preset . is_dirty = false ;
2017-10-25 10:53:31 +00:00
if ( select )
this - > select_preset_by_name ( name , true ) ;
return preset ;
}
2020-03-06 16:41:00 +00:00
void PresetCollection : : save_current_preset ( const std : : string & new_name , bool detach )
2017-10-26 15:17:39 +00:00
{
2019-08-06 14:51:32 +00:00
// 1) Find the preset with a new_name or create a new one,
// initialize it with the edited config.
2017-12-06 15:47:53 +00:00
auto it = this - > find_preset_internal ( new_name ) ;
if ( it ! = m_presets . end ( ) & & it - > name = = new_name ) {
2017-10-26 15:17:39 +00:00
// Preset with the same name found.
Preset & preset = * it ;
2018-03-12 15:04:32 +00:00
if ( preset . is_default | | preset . is_external | | preset . is_system )
2017-10-26 15:17:39 +00:00
// Cannot overwrite the default preset.
return ;
// Overwriting an existing preset.
preset . config = std : : move ( m_edited_preset . config ) ;
2019-08-06 14:51:32 +00:00
// The newly saved preset will be activated -> make it visible.
preset . is_visible = true ;
2020-03-06 16:41:00 +00:00
if ( detach ) {
// Clear the link to the parent profile.
preset . vendor = nullptr ;
preset . inherits ( ) . clear ( ) ;
preset . alias . clear ( ) ;
preset . renamed_from . clear ( ) ;
}
2017-10-26 15:17:39 +00:00
} else {
// Creating a new preset.
2019-08-06 14:51:32 +00:00
Preset & preset = * m_presets . insert ( it , m_edited_preset ) ;
2018-06-27 14:57:42 +00:00
std : : string & inherits = preset . inherits ( ) ;
2018-03-12 15:04:32 +00:00
std : : string old_name = preset . name ;
2017-10-26 15:17:39 +00:00
preset . name = new_name ;
2019-08-06 14:51:32 +00:00
preset . file = this - > path_from_name ( new_name ) ;
2018-03-12 15:04:32 +00:00
preset . vendor = nullptr ;
2020-03-06 16:41:00 +00:00
preset . alias . clear ( ) ;
preset . renamed_from . clear ( ) ;
if ( detach ) {
// Clear the link to the parent profile.
inherits . clear ( ) ;
} else if ( preset . is_system ) {
2018-03-12 15:04:32 +00:00
// Inheriting from a system preset.
inherits = /* preset.vendor->name + "/" + */ old_name ;
} else if ( inherits . empty ( ) ) {
// Inheriting from a user preset. Link the new preset to the old preset.
2018-03-13 08:45:04 +00:00
// inherits = old_name;
2018-03-12 15:04:32 +00:00
} else {
2019-08-06 14:51:32 +00:00
// Inherited from a user preset. Just maintain the "inherited" flag,
2018-03-12 15:04:32 +00:00
// meaning it will inherit from either the system preset, or the inherited user preset.
}
preset . is_default = false ;
preset . is_system = false ;
preset . is_external = false ;
2019-08-06 14:51:32 +00:00
// The newly saved preset will be activated -> make it visible.
preset . is_visible = true ;
2019-11-29 10:02:30 +00:00
// Just system presets have aliases
preset . alias . clear ( ) ;
2019-08-06 14:51:32 +00:00
}
// 2) Activate the saved preset.
this - > select_preset_by_name ( new_name , true ) ;
// 2) Store the active preset to disk.
this - > get_selected_preset ( ) . save ( ) ;
2017-10-26 15:17:39 +00:00
}
2019-03-22 11:11:23 +00:00
bool PresetCollection : : delete_current_preset ( )
2017-10-26 15:17:39 +00:00
{
const Preset & selected = this - > get_selected_preset ( ) ;
2019-08-06 14:51:32 +00:00
if ( selected . is_default )
return false ;
if ( ! selected . is_external & & ! selected . is_system ) {
// Erase the preset file.
boost : : nowide : : remove ( selected . file . c_str ( ) ) ;
}
2017-10-26 15:17:39 +00:00
// Remove the preset from the list.
m_presets . erase ( m_presets . begin ( ) + m_idx_selected ) ;
// Find the next visible preset.
2017-11-02 15:21:34 +00:00
size_t new_selected_idx = m_idx_selected ;
if ( new_selected_idx < m_presets . size ( ) )
for ( ; new_selected_idx < m_presets . size ( ) & & ! m_presets [ new_selected_idx ] . is_visible ; + + new_selected_idx ) ;
if ( new_selected_idx = = m_presets . size ( ) )
2019-08-06 14:51:32 +00:00
for ( - - new_selected_idx ; new_selected_idx > 0 & & ! m_presets [ new_selected_idx ] . is_visible ; - - new_selected_idx ) ;
2017-11-02 15:21:34 +00:00
this - > select_preset ( new_selected_idx ) ;
2019-08-06 14:51:32 +00:00
return true ;
2017-10-26 15:17:39 +00:00
}
2019-10-02 20:50:55 +00:00
bool PresetCollection : : delete_preset ( const std : : string & name )
{
auto it = this - > find_preset_internal ( name ) ;
const Preset & preset = * it ;
if ( preset . is_default )
return false ;
if ( ! preset . is_external & & ! preset . is_system ) {
// Erase the preset file.
boost : : nowide : : remove ( preset . file . c_str ( ) ) ;
}
m_presets . erase ( it ) ;
return true ;
}
2018-03-12 15:04:32 +00:00
const Preset * PresetCollection : : get_selected_preset_parent ( ) const
{
2020-04-22 10:49:52 +00:00
if ( this - > get_selected_idx ( ) = = size_t ( - 1 ) )
2019-05-15 15:15:52 +00:00
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr ;
2019-07-01 12:56:28 +00:00
2020-01-02 15:40:32 +00:00
const Preset & selected_preset = this - > get_selected_preset ( ) ;
if ( selected_preset . is_system | | selected_preset . is_default )
return & selected_preset ;
2019-08-06 14:51:32 +00:00
2020-01-02 15:40:32 +00:00
const Preset & edited_preset = this - > get_edited_preset ( ) ;
const std : : string & inherits = edited_preset . inherits ( ) ;
const Preset * preset = nullptr ;
if ( inherits . empty ( ) ) {
if ( selected_preset . is_external )
return nullptr ;
preset = & this - > default_preset ( m_type = = Preset : : Type : : TYPE_PRINTER & & edited_preset . printer_technology ( ) = = ptSLA ? 1 : 0 ) ;
} else
preset = this - > find_preset ( inherits , false ) ;
if ( preset = = nullptr ) {
// Resolve the "renamed_from" field.
assert ( ! inherits . empty ( ) ) ;
auto it = this - > find_preset_renamed ( inherits ) ;
2021-02-24 08:22:31 +00:00
if ( it ! = m_presets . end ( ) )
2020-01-02 15:40:32 +00:00
preset = & ( * it ) ;
2019-07-01 12:56:28 +00:00
}
return ( preset = = nullptr /* || preset->is_default*/ | | preset - > is_external ) ? nullptr : preset ;
2018-03-12 15:04:32 +00:00
}
2018-04-09 14:50:17 +00:00
const Preset * PresetCollection : : get_preset_parent ( const Preset & child ) const
{
2018-06-27 14:57:42 +00:00
const std : : string & inherits = child . inherits ( ) ;
if ( inherits . empty ( ) )
2019-08-06 14:51:32 +00:00
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr ;
2018-06-27 14:57:42 +00:00
const Preset * preset = this - > find_preset ( inherits , false ) ;
2020-01-02 15:40:32 +00:00
if ( preset = = nullptr ) {
auto it = this - > find_preset_renamed ( inherits ) ;
2021-02-24 08:22:31 +00:00
if ( it ! = m_presets . end ( ) )
2020-01-02 15:40:32 +00:00
preset = & ( * it ) ;
}
2021-02-24 08:22:31 +00:00
return
2020-12-07 16:09:02 +00:00
// not found
2021-02-24 08:22:31 +00:00
( preset = = nullptr /* || preset->is_default */ | |
2020-12-07 16:09:02 +00:00
// this should not happen, user profile should not derive from an external profile
preset - > is_external | |
// this should not happen, however people are creative, see GH #4996
2021-02-24 08:22:31 +00:00
preset = = & child ) ?
nullptr :
2020-12-07 16:09:02 +00:00
preset ;
2018-04-09 14:50:17 +00:00
}
2019-12-05 13:48:11 +00:00
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile PresetCollection : : get_preset_with_vendor_profile ( const Preset & preset ) const
{
const Preset * p = & preset ;
const VendorProfile * v = nullptr ;
do {
if ( p - > vendor ! = nullptr ) {
v = p - > vendor ;
break ;
}
p = this - > get_preset_parent ( * p ) ;
} while ( p ! = nullptr ) ;
return PresetWithVendorProfile ( preset , v ) ;
}
2019-11-29 10:02:30 +00:00
const std : : string & PresetCollection : : get_preset_name_by_alias ( const std : : string & alias ) const
2019-11-25 14:57:36 +00:00
{
2020-01-23 15:04:17 +00:00
for (
// Find the 1st profile name with the alias.
auto it = Slic3r : : lower_bound_by_predicate ( m_map_alias_to_profile_name . begin ( ) , m_map_alias_to_profile_name . end ( ) , [ & alias ] ( auto & l ) { return l . first < alias ; } ) ;
// Continue over all profile names with the same alias.
it ! = m_map_alias_to_profile_name . end ( ) & & it - > first = = alias ; + + it )
if ( auto it_preset = this - > find_preset_internal ( it - > second ) ;
2021-02-24 08:22:31 +00:00
it_preset ! = m_presets . end ( ) & & it_preset - > name = = it - > second & &
2020-03-03 13:52:16 +00:00
it_preset - > is_visible & & ( it_preset - > is_compatible | | size_t ( it_preset - m_presets . begin ( ) ) = = m_idx_selected ) )
2020-01-23 15:04:17 +00:00
return it_preset - > name ;
2019-11-25 14:57:36 +00:00
return alias ;
}
2020-03-14 17:35:42 +00:00
const std : : string * PresetCollection : : get_preset_name_renamed ( const std : : string & old_name ) const
{
auto it_renamed = m_map_system_profile_renamed . find ( old_name ) ;
if ( it_renamed ! = m_map_system_profile_renamed . end ( ) )
return & it_renamed - > second ;
return nullptr ;
}
2018-04-06 13:42:52 +00:00
const std : : string & PresetCollection : : get_suffix_modified ( ) {
2019-08-06 14:51:32 +00:00
return g_suffix_modified ;
2018-04-06 13:42:52 +00:00
}
2017-10-25 10:53:31 +00:00
// Return a preset by its name. If the preset is active, a temporary copy is returned.
// If a preset is not found by its name, null is returned.
Preset * PresetCollection : : find_preset ( const std : : string & name , bool first_visible_if_not_found )
{
Preset key ( m_type , name , false ) ;
2017-12-06 15:47:53 +00:00
auto it = this - > find_preset_internal ( name ) ;
2017-10-25 10:53:31 +00:00
// Ensure that a temporary copy is returned if the preset found is currently selected.
2019-08-06 14:51:32 +00:00
return ( it ! = m_presets . end ( ) & & it - > name = = key . name ) ? & this - > preset ( it - m_presets . begin ( ) ) :
2017-10-25 10:53:31 +00:00
first_visible_if_not_found ? & this - > first_visible ( ) : nullptr ;
}
// Return index of the first visible preset. Certainly at least the '- default -' preset shall be visible.
size_t PresetCollection : : first_visible_idx ( ) const
{
2018-08-01 09:09:51 +00:00
size_t idx = m_default_suppressed ? m_num_default_presets : 0 ;
2021-05-06 12:43:36 +00:00
for ( ; idx < m_presets . size ( ) ; + + idx )
2017-10-25 10:53:31 +00:00
if ( m_presets [ idx ] . is_visible )
break ;
2018-09-10 07:11:49 +00:00
if ( idx = = m_presets . size ( ) )
2017-10-25 10:53:31 +00:00
idx = 0 ;
return idx ;
2017-10-02 15:35:00 +00:00
}
2017-09-19 11:55:48 +00:00
void PresetCollection : : set_default_suppressed ( bool default_suppressed )
{
if ( m_default_suppressed ! = default_suppressed ) {
m_default_suppressed = default_suppressed ;
2019-08-06 14:51:32 +00:00
bool default_visible = ! default_suppressed | | m_idx_selected < m_num_default_presets ;
for ( size_t i = 0 ; i < m_num_default_presets ; + + i )
m_presets [ i ] . is_visible = default_visible ;
2017-09-19 11:55:48 +00:00
}
}
Various changes in handling of profile compatiblilities
and the "show incompatible profiles" option.
It was not able to select the incompatible Print profile, which is
possible now.
(see Cannot select incompatible printer profile #3715)
When the Printer profile derived from the Prusa3D system profile was
active or a system Prusa3D profile was active, and when the Print profile
with the removed "inherits" field was active (or any other profile
derived from the "-- default --" profile was active), then the filament
selector offered just the profiles with the removed "inherits" field
(or any other profile derived from the "-- default--") profile.
This behavior has been now changed, so that in this scenario the Filament selector
will offer the Prusa3D vendor profiles compatible with the active Print
and Printer profile as well as the user profiles.
Slicer was also changed to keep an incompatible preset selected
at its respective tab if its respective "Red flag" is enabled.
For example, if an incompatible Print preset is selected and a Printer
profile is switched to another one which is not compatible with the active
Print preset that was red already, the active Print preset is
not switched if the Print "Red flag" is active. However, if the Print profile
was compatible before the Printer profile is switched and now the Print
profile becomes incompatible, another compatible Print profile is selected.
A likely bug in wxWidgets was worked around when switching a Print preset
on Plater, if the last item in the Print preset was active and incompatible,
and another Print preset was selected by the user. On Windows, an CBN_EDITCHANGE
is sent just after combo box selection change event and the CBN_EDITCHANGE
holds an index of the combo box item, which will be removed by the 1st event,
therefore leading to an assert in wxWidgets on CBN_EDITCHANGE. The workaround
is to disable processing of CBN_EDITCHANGE on Windows for the Plater
preset selection combo boxes.
2020-02-27 10:44:01 +00:00
size_t PresetCollection : : update_compatible_internal ( const PresetWithVendorProfile & active_printer , const PresetWithVendorProfile * active_print , PresetSelectCompatibleType unselect_if_incompatible )
2017-09-19 11:55:48 +00:00
{
2017-12-19 15:48:14 +00:00
DynamicPrintConfig config ;
2019-12-05 13:48:11 +00:00
config . set_key_value ( " printer_preset " , new ConfigOptionString ( active_printer . preset . name ) ) ;
const ConfigOption * opt = active_printer . preset . config . option ( " nozzle_diameter " ) ;
2018-08-01 09:09:51 +00:00
if ( opt )
config . set_key_value ( " num_extruders " , new ConfigOptionInt ( ( int ) static_cast < const ConfigOptionFloats * > ( opt ) - > values . size ( ) ) ) ;
2020-03-06 16:41:00 +00:00
bool some_compatible = false ;
2018-08-01 09:09:51 +00:00
for ( size_t idx_preset = m_num_default_presets ; idx_preset < m_presets . size ( ) ; + + idx_preset ) {
2017-11-10 16:27:05 +00:00
bool selected = idx_preset = = m_idx_selected ;
Preset & preset_selected = m_presets [ idx_preset ] ;
Preset & preset_edited = selected ? m_edited_preset : preset_selected ;
2019-11-19 11:44:55 +00:00
2019-12-05 13:48:11 +00:00
const PresetWithVendorProfile this_preset_with_vendor_profile = this - > get_preset_with_vendor_profile ( preset_edited ) ;
Various changes in handling of profile compatiblilities
and the "show incompatible profiles" option.
It was not able to select the incompatible Print profile, which is
possible now.
(see Cannot select incompatible printer profile #3715)
When the Printer profile derived from the Prusa3D system profile was
active or a system Prusa3D profile was active, and when the Print profile
with the removed "inherits" field was active (or any other profile
derived from the "-- default --" profile was active), then the filament
selector offered just the profiles with the removed "inherits" field
(or any other profile derived from the "-- default--") profile.
This behavior has been now changed, so that in this scenario the Filament selector
will offer the Prusa3D vendor profiles compatible with the active Print
and Printer profile as well as the user profiles.
Slicer was also changed to keep an incompatible preset selected
at its respective tab if its respective "Red flag" is enabled.
For example, if an incompatible Print preset is selected and a Printer
profile is switched to another one which is not compatible with the active
Print preset that was red already, the active Print preset is
not switched if the Print "Red flag" is active. However, if the Print profile
was compatible before the Printer profile is switched and now the Print
profile becomes incompatible, another compatible Print profile is selected.
A likely bug in wxWidgets was worked around when switching a Print preset
on Plater, if the last item in the Print preset was active and incompatible,
and another Print preset was selected by the user. On Windows, an CBN_EDITCHANGE
is sent just after combo box selection change event and the CBN_EDITCHANGE
holds an index of the combo box item, which will be removed by the 1st event,
therefore leading to an assert in wxWidgets on CBN_EDITCHANGE. The workaround
is to disable processing of CBN_EDITCHANGE on Windows for the Plater
preset selection combo boxes.
2020-02-27 10:44:01 +00:00
bool was_compatible = preset_edited . is_compatible ;
2019-12-05 13:48:11 +00:00
preset_edited . is_compatible = is_compatible_with_printer ( this_preset_with_vendor_profile , active_printer , & config ) ;
2020-03-06 16:41:00 +00:00
some_compatible | = preset_edited . is_compatible ;
2019-12-05 13:48:11 +00:00
if ( active_print ! = nullptr )
Various changes in handling of profile compatiblilities
and the "show incompatible profiles" option.
It was not able to select the incompatible Print profile, which is
possible now.
(see Cannot select incompatible printer profile #3715)
When the Printer profile derived from the Prusa3D system profile was
active or a system Prusa3D profile was active, and when the Print profile
with the removed "inherits" field was active (or any other profile
derived from the "-- default --" profile was active), then the filament
selector offered just the profiles with the removed "inherits" field
(or any other profile derived from the "-- default--") profile.
This behavior has been now changed, so that in this scenario the Filament selector
will offer the Prusa3D vendor profiles compatible with the active Print
and Printer profile as well as the user profiles.
Slicer was also changed to keep an incompatible preset selected
at its respective tab if its respective "Red flag" is enabled.
For example, if an incompatible Print preset is selected and a Printer
profile is switched to another one which is not compatible with the active
Print preset that was red already, the active Print preset is
not switched if the Print "Red flag" is active. However, if the Print profile
was compatible before the Printer profile is switched and now the Print
profile becomes incompatible, another compatible Print profile is selected.
A likely bug in wxWidgets was worked around when switching a Print preset
on Plater, if the last item in the Print preset was active and incompatible,
and another Print preset was selected by the user. On Windows, an CBN_EDITCHANGE
is sent just after combo box selection change event and the CBN_EDITCHANGE
holds an index of the combo box item, which will be removed by the 1st event,
therefore leading to an assert in wxWidgets on CBN_EDITCHANGE. The workaround
is to disable processing of CBN_EDITCHANGE on Windows for the Plater
preset selection combo boxes.
2020-02-27 10:44:01 +00:00
preset_edited . is_compatible & = is_compatible_with_print ( this_preset_with_vendor_profile , * active_print , active_printer ) ;
2021-02-24 08:22:31 +00:00
if ( ! preset_edited . is_compatible & & selected & &
Various changes in handling of profile compatiblilities
and the "show incompatible profiles" option.
It was not able to select the incompatible Print profile, which is
possible now.
(see Cannot select incompatible printer profile #3715)
When the Printer profile derived from the Prusa3D system profile was
active or a system Prusa3D profile was active, and when the Print profile
with the removed "inherits" field was active (or any other profile
derived from the "-- default --" profile was active), then the filament
selector offered just the profiles with the removed "inherits" field
(or any other profile derived from the "-- default--") profile.
This behavior has been now changed, so that in this scenario the Filament selector
will offer the Prusa3D vendor profiles compatible with the active Print
and Printer profile as well as the user profiles.
Slicer was also changed to keep an incompatible preset selected
at its respective tab if its respective "Red flag" is enabled.
For example, if an incompatible Print preset is selected and a Printer
profile is switched to another one which is not compatible with the active
Print preset that was red already, the active Print preset is
not switched if the Print "Red flag" is active. However, if the Print profile
was compatible before the Printer profile is switched and now the Print
profile becomes incompatible, another compatible Print profile is selected.
A likely bug in wxWidgets was worked around when switching a Print preset
on Plater, if the last item in the Print preset was active and incompatible,
and another Print preset was selected by the user. On Windows, an CBN_EDITCHANGE
is sent just after combo box selection change event and the CBN_EDITCHANGE
holds an index of the combo box item, which will be removed by the 1st event,
therefore leading to an assert in wxWidgets on CBN_EDITCHANGE. The workaround
is to disable processing of CBN_EDITCHANGE on Windows for the Plater
preset selection combo boxes.
2020-02-27 10:44:01 +00:00
( unselect_if_incompatible = = PresetSelectCompatibleType : : Always | | ( unselect_if_incompatible = = PresetSelectCompatibleType : : OnlyIfWasCompatible & & was_compatible ) ) )
2019-11-19 11:44:55 +00:00
m_idx_selected = size_t ( - 1 ) ;
2017-11-10 16:27:05 +00:00
if ( selected )
preset_selected . is_compatible = preset_edited . is_compatible ;
}
2020-03-06 16:41:00 +00:00
// Update visibility of the default profiles here if the defaults are suppressed, the current profile is not compatible and we don't want to select another compatible profile.
if ( m_idx_selected > = m_num_default_presets & & m_default_suppressed )
for ( size_t i = 0 ; i < m_num_default_presets ; + + i )
m_presets [ i ] . is_visible = ! some_compatible ;
2018-04-13 12:49:33 +00:00
return m_idx_selected ;
2017-09-19 11:55:48 +00:00
}
2020-06-16 10:57:49 +00:00
// Update a dirty flag of the current preset
2017-10-25 10:53:31 +00:00
// Return true if the dirty flag changed.
2020-06-16 10:57:49 +00:00
bool PresetCollection : : update_dirty ( )
2017-10-25 10:53:31 +00:00
{
bool was_dirty = this - > get_selected_preset ( ) . is_dirty ;
bool is_dirty = current_is_dirty ( ) ;
this - > get_selected_preset ( ) . is_dirty = is_dirty ;
2017-11-01 18:30:05 +00:00
this - > get_edited_preset ( ) . is_dirty = is_dirty ;
2020-06-16 10:57:49 +00:00
2017-10-25 10:53:31 +00:00
return was_dirty ! = is_dirty ;
}
2018-12-04 16:56:49 +00:00
template < class T >
void add_correct_opts_to_diff ( const std : : string & opt_key , t_config_option_keys & vec , const ConfigBase & other , const ConfigBase & this_c )
{
2019-08-06 14:51:32 +00:00
const T * opt_init = static_cast < const T * > ( other . option ( opt_key ) ) ;
const T * opt_cur = static_cast < const T * > ( this_c . option ( opt_key ) ) ;
int opt_init_max_id = opt_init - > values . size ( ) - 1 ;
2019-11-19 11:44:55 +00:00
for ( int i = 0 ; i < int ( opt_cur - > values . size ( ) ) ; i + + )
2019-08-06 14:51:32 +00:00
{
int init_id = i < = opt_init_max_id ? i : 0 ;
if ( opt_cur - > values [ i ] ! = opt_init - > values [ init_id ] )
vec . emplace_back ( opt_key + " # " + std : : to_string ( i ) ) ;
}
2018-12-04 16:56:49 +00:00
}
// Use deep_diff to correct return of changed options, considering individual options for each extruder.
inline t_config_option_keys deep_diff ( const ConfigBase & config_this , const ConfigBase & config_other )
{
t_config_option_keys diff ;
for ( const t_config_option_key & opt_key : config_this . keys ( ) ) {
const ConfigOption * this_opt = config_this . option ( opt_key ) ;
const ConfigOption * other_opt = config_other . option ( opt_key ) ;
if ( this_opt ! = nullptr & & other_opt ! = nullptr & & * this_opt ! = * other_opt )
{
2020-02-24 09:41:20 +00:00
if ( opt_key = = " bed_shape " | | opt_key = = " thumbnails " | | opt_key = = " compatible_prints " | | opt_key = = " compatible_printers " ) {
2021-01-04 10:13:10 +00:00
// Scalar variable, or a vector variable, which is independent from number of extruders,
// thus the vector is presented to the user as a single input.
2018-12-04 16:56:49 +00:00
diff . emplace_back ( opt_key ) ;
2021-01-04 10:13:10 +00:00
} else if ( opt_key = = " default_filament_profile " ) {
// Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help.
// Also the length of this field may differ, which may lead to a crash if the block below is used.
} else {
switch ( other_opt - > type ( ) ) {
case coInts : add_correct_opts_to_diff < ConfigOptionInts > ( opt_key , diff , config_other , config_this ) ; break ;
case coBools : add_correct_opts_to_diff < ConfigOptionBools > ( opt_key , diff , config_other , config_this ) ; break ;
case coFloats : add_correct_opts_to_diff < ConfigOptionFloats > ( opt_key , diff , config_other , config_this ) ; break ;
case coStrings : add_correct_opts_to_diff < ConfigOptionStrings > ( opt_key , diff , config_other , config_this ) ; break ;
case coPercents : add_correct_opts_to_diff < ConfigOptionPercents > ( opt_key , diff , config_other , config_this ) ; break ;
case coPoints : add_correct_opts_to_diff < ConfigOptionPoints > ( opt_key , diff , config_other , config_this ) ; break ;
default : diff . emplace_back ( opt_key ) ; break ;
}
2018-12-04 16:56:49 +00:00
}
}
}
return diff ;
}
2021-08-23 08:47:40 +00:00
static constexpr const std : : initializer_list < const char * > optional_keys { " compatible_prints " , " compatible_printers " } ;
bool PresetCollection : : is_dirty ( const Preset * edited , const Preset * reference )
{
if ( edited ! = nullptr & & reference ! = nullptr ) {
// Only compares options existing in both configs.
if ( ! reference - > config . equals ( edited - > config ) )
return true ;
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
for ( auto & opt_key : optional_keys )
if ( reference - > config . has ( opt_key ) ! = edited - > config . has ( opt_key ) )
return true ;
}
return false ;
}
2018-08-08 15:47:59 +00:00
std : : vector < std : : string > PresetCollection : : dirty_options ( const Preset * edited , const Preset * reference , const bool deep_compare /*= false*/ )
2018-03-12 15:04:32 +00:00
{
std : : vector < std : : string > changed ;
2019-08-06 14:51:32 +00:00
if ( edited ! = nullptr & & reference ! = nullptr ) {
2021-08-23 08:47:40 +00:00
// Only compares options existing in both configs.
2018-08-08 15:47:59 +00:00
changed = deep_compare ?
2019-08-06 14:51:32 +00:00
deep_diff ( edited - > config , reference - > config ) :
reference - > config . diff ( edited - > config ) ;
2018-03-12 15:04:32 +00:00
// The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer.
// If the key exists and it is empty, it means it is compatible with no printer.
2021-08-23 08:47:40 +00:00
for ( auto & opt_key : optional_keys )
2018-03-12 15:04:32 +00:00
if ( reference - > config . has ( opt_key ) ! = edited - > config . has ( opt_key ) )
changed . emplace_back ( opt_key ) ;
2017-12-18 14:07:38 +00:00
}
2017-12-13 13:44:27 +00:00
return changed ;
}
2017-12-13 13:00:14 +00:00
// Select a new preset. This resets all the edits done to the currently selected preset.
// If the preset with index idx does not exist, a first visible preset is selected.
2017-10-25 10:53:31 +00:00
Preset & PresetCollection : : select_preset ( size_t idx )
{
2017-11-02 15:21:34 +00:00
for ( Preset & preset : m_presets )
preset . is_dirty = false ;
2017-10-25 10:53:31 +00:00
if ( idx > = m_presets . size ( ) )
idx = first_visible_idx ( ) ;
m_idx_selected = idx ;
m_edited_preset = m_presets [ idx ] ;
2021-04-06 14:29:05 +00:00
update_saved_preset_from_current_preset ( ) ;
2019-08-06 14:51:32 +00:00
bool default_visible = ! m_default_suppressed | | m_idx_selected < m_num_default_presets ;
for ( size_t i = 0 ; i < m_num_default_presets ; + + i )
m_presets [ i ] . is_visible = default_visible ;
2017-10-25 10:53:31 +00:00
return m_presets [ idx ] ;
}
2017-10-26 15:17:39 +00:00
bool PresetCollection : : select_preset_by_name ( const std : : string & name_w_suffix , bool force )
2019-08-06 14:51:32 +00:00
{
2017-10-30 17:41:50 +00:00
std : : string name = Preset : : remove_suffix_modified ( name_w_suffix ) ;
2017-10-25 10:53:31 +00:00
// 1) Try to find the preset by its name.
2017-12-06 15:47:53 +00:00
auto it = this - > find_preset_internal ( name ) ;
2017-10-25 10:53:31 +00:00
size_t idx = 0 ;
2019-08-06 14:51:32 +00:00
if ( it ! = m_presets . end ( ) & & it - > name = = name & & it - > is_visible )
2018-04-13 12:49:33 +00:00
// Preset found by its name and it is visible.
2017-10-25 10:53:31 +00:00
idx = it - m_presets . begin ( ) ;
else {
// Find the first visible preset.
2018-08-01 09:09:51 +00:00
for ( size_t i = m_default_suppressed ? m_num_default_presets : 0 ; i < m_presets . size ( ) ; + + i )
2017-10-25 10:53:31 +00:00
if ( m_presets [ i ] . is_visible ) {
idx = i ;
break ;
}
// If the first visible preset was not found, return the 0th element, which is the default preset.
}
// 2) Select the new preset.
if ( m_idx_selected ! = idx | | force ) {
this - > select_preset ( idx ) ;
return true ;
}
return false ;
}
2018-04-13 12:49:33 +00:00
bool PresetCollection : : select_preset_by_name_strict ( const std : : string & name )
2019-08-06 14:51:32 +00:00
{
2018-04-13 12:49:33 +00:00
// 1) Try to find the preset by its name.
auto it = this - > find_preset_internal ( name ) ;
size_t idx = ( size_t ) - 1 ;
2019-08-06 14:51:32 +00:00
if ( it ! = m_presets . end ( ) & & it - > name = = name & & it - > is_visible )
2018-04-13 12:49:33 +00:00
// Preset found by its name.
idx = it - m_presets . begin ( ) ;
// 2) Select the new preset.
if ( idx ! = ( size_t ) - 1 ) {
this - > select_preset ( idx ) ;
return true ;
}
m_idx_selected = idx ;
return false ;
}
2018-04-18 11:35:51 +00:00
// Merge one vendor's presets with the other vendor's presets, report duplicates.
2019-06-17 14:39:22 +00:00
std : : vector < std : : string > PresetCollection : : merge_presets ( PresetCollection & & other , const VendorMap & new_vendors )
2018-04-18 11:35:51 +00:00
{
std : : vector < std : : string > duplicates ;
for ( Preset & preset : other . m_presets ) {
if ( preset . is_default | | preset . is_external )
continue ;
Preset key ( m_type , preset . name ) ;
2018-08-01 09:09:51 +00:00
auto it = std : : lower_bound ( m_presets . begin ( ) + m_num_default_presets , m_presets . end ( ) , key ) ;
2018-04-18 11:35:51 +00:00
if ( it = = m_presets . end ( ) | | it - > name ! = preset . name ) {
if ( preset . vendor ! = nullptr ) {
// Re-assign a pointer to the vendor structure in the new PresetBundle.
2019-06-17 14:39:22 +00:00
auto it = new_vendors . find ( preset . vendor - > id ) ;
2018-04-18 11:35:51 +00:00
assert ( it ! = new_vendors . end ( ) ) ;
2019-06-17 14:39:22 +00:00
preset . vendor = & it - > second ;
2018-04-18 11:35:51 +00:00
}
2021-05-06 12:43:36 +00:00
m_presets . emplace ( it , std : : move ( preset ) ) ;
2018-04-18 11:35:51 +00:00
} else
duplicates . emplace_back ( std : : move ( preset . name ) ) ;
}
return duplicates ;
}
2021-02-04 08:42:32 +00:00
void PresetCollection : : update_vendor_ptrs_after_copy ( const VendorMap & new_vendors )
{
for ( Preset & preset : m_presets )
if ( preset . vendor ! = nullptr ) {
assert ( ! preset . is_default & & ! preset . is_external ) ;
// Re-assign a pointer to the vendor structure in the new PresetBundle.
auto it = new_vendors . find ( preset . vendor - > id ) ;
assert ( it ! = new_vendors . end ( ) ) ;
preset . vendor = & it - > second ;
}
}
2020-01-23 15:04:17 +00:00
void PresetCollection : : update_map_alias_to_profile_name ( )
{
m_map_alias_to_profile_name . clear ( ) ;
for ( const Preset & preset : m_presets )
m_map_alias_to_profile_name . emplace_back ( preset . alias , preset . name ) ;
std : : sort ( m_map_alias_to_profile_name . begin ( ) , m_map_alias_to_profile_name . end ( ) , [ ] ( auto & l , auto & r ) { return l . first < r . first ; } ) ;
}
2020-01-02 15:40:32 +00:00
void PresetCollection : : update_map_system_profile_renamed ( )
{
m_map_system_profile_renamed . clear ( ) ;
for ( Preset & preset : m_presets )
for ( const std : : string & renamed_from : preset . renamed_from ) {
const auto [ it , success ] = m_map_system_profile_renamed . insert ( std : : pair < std : : string , std : : string > ( renamed_from , preset . name ) ) ;
if ( ! success )
BOOST_LOG_TRIVIAL ( error ) < < boost : : format ( " Preset name \" %1% \" was marked as renamed from \" %2% \" , though preset name \" %3% \" was marked as renamed from \" %2% \" as well. " ) % preset . name % renamed_from % it - > second ;
}
}
2017-10-25 10:53:31 +00:00
std : : string PresetCollection : : name ( ) const
{
switch ( this - > type ( ) ) {
2019-01-21 11:34:28 +00:00
case Preset : : TYPE_PRINT : return L ( " print " ) ;
case Preset : : TYPE_FILAMENT : return L ( " filament " ) ;
case Preset : : TYPE_SLA_PRINT : return L ( " SLA print " ) ;
2019-01-31 14:09:16 +00:00
case Preset : : TYPE_SLA_MATERIAL : return L ( " SLA material " ) ;
2019-01-21 11:34:28 +00:00
case Preset : : TYPE_PRINTER : return L ( " printer " ) ;
2018-12-04 16:56:49 +00:00
default : return " invalid " ;
2017-10-25 10:53:31 +00:00
}
}
2019-02-03 19:17:55 +00:00
std : : string PresetCollection : : section_name ( ) const
{
switch ( this - > type ( ) ) {
case Preset : : TYPE_PRINT : return " print " ;
case Preset : : TYPE_FILAMENT : return " filament " ;
case Preset : : TYPE_SLA_PRINT : return " sla_print " ;
case Preset : : TYPE_SLA_MATERIAL : return " sla_material " ;
case Preset : : TYPE_PRINTER : return " printer " ;
default : return " invalid " ;
}
}
2020-12-08 16:45:50 +00:00
// Used for validating the "inherits" flag when importing user's config bundles.
// Returns names of all system presets including the former names of these presets.
2019-01-08 09:52:11 +00:00
std : : vector < std : : string > PresetCollection : : system_preset_names ( ) const
{
size_t num = 0 ;
for ( const Preset & preset : m_presets )
if ( preset . is_system )
+ + num ;
std : : vector < std : : string > out ;
out . reserve ( num ) ;
2019-08-06 14:51:32 +00:00
for ( const Preset & preset : m_presets )
2020-12-08 16:45:50 +00:00
if ( preset . is_system ) {
2019-08-06 14:51:32 +00:00
out . emplace_back ( preset . name ) ;
2020-12-08 16:45:50 +00:00
out . insert ( out . end ( ) , preset . renamed_from . begin ( ) , preset . renamed_from . end ( ) ) ;
}
2019-01-08 09:52:11 +00:00
std : : sort ( out . begin ( ) , out . end ( ) ) ;
return out ;
}
2017-12-20 10:28:16 +00:00
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std : : string PresetCollection : : path_from_name ( const std : : string & new_name ) const
{
2019-08-06 14:51:32 +00:00
std : : string file_name = boost : : iends_with ( new_name , " .ini " ) ? new_name : ( new_name + " .ini " ) ;
2017-12-20 10:28:16 +00:00
return ( boost : : filesystem : : path ( m_dir_path ) / file_name ) . make_preferred ( ) . string ( ) ;
}
2019-08-06 14:51:32 +00:00
const Preset & PrinterPresetCollection : : default_preset_for ( const DynamicPrintConfig & config ) const
{
2018-10-31 15:22:36 +00:00
const ConfigOptionEnumGeneric * opt_printer_technology = config . opt < ConfigOptionEnumGeneric > ( " printer_technology " ) ;
2019-08-06 14:51:32 +00:00
return this - > default_preset ( ( opt_printer_technology = = nullptr | | opt_printer_technology - > value = = ptFFF ) ? 0 : 1 ) ;
2018-10-31 15:22:36 +00:00
}
2021-08-18 14:10:35 +00:00
const Preset * PrinterPresetCollection : : find_system_preset_by_model_and_variant ( const std : : string & model_id , const std : : string & variant ) const
2019-04-26 14:53:04 +00:00
{
if ( model_id . empty ( ) ) { return nullptr ; }
const auto it = std : : find_if ( cbegin ( ) , cend ( ) , [ & ] ( const Preset & preset ) {
2021-08-18 14:10:35 +00:00
if ( ! preset . is_system | | preset . config . opt_string ( " printer_model " ) ! = model_id )
return false ;
if ( variant . empty ( ) )
return true ;
return preset . config . opt_string ( " printer_variant " ) = = variant ;
2019-04-26 14:53:04 +00:00
} ) ;
return it ! = cend ( ) ? & * it : nullptr ;
}
2020-06-24 06:50:01 +00:00
// -------------------------
// *** PhysicalPrinter ***
// -------------------------
2020-07-14 13:34:08 +00:00
std : : string PhysicalPrinter : : separator ( )
{
return " * " ;
}
2021-08-24 11:10:35 +00:00
static std : : vector < std : : string > s_PhysicalPrinter_opts {
" preset_name " , // temporary option to compatibility with older Slicer
" preset_names " ,
" printer_technology " ,
" host_type " ,
" print_host " ,
" printhost_apikey " ,
" printhost_cafile " ,
" printhost_port " ,
" printhost_authorization_type " ,
// HTTP digest authentization (RFC 2617)
" printhost_user " ,
" printhost_password " ,
2021-08-24 11:57:52 +00:00
" printhost_ssl_ignore_revoke "
2021-08-24 11:10:35 +00:00
} ;
2020-06-24 06:50:01 +00:00
const std : : vector < std : : string > & PhysicalPrinter : : printer_options ( )
2020-06-16 10:57:49 +00:00
{
2021-08-24 11:10:35 +00:00
return s_PhysicalPrinter_opts ;
2020-06-24 06:50:01 +00:00
}
2020-10-28 08:51:05 +00:00
static constexpr auto legacy_print_host_options = {
" print_host " ,
" printhost_apikey " ,
" printhost_cafile " ,
} ;
2020-07-21 14:21:18 +00:00
2020-07-28 09:31:16 +00:00
std : : vector < std : : string > PhysicalPrinter : : presets_with_print_host_information ( const PrinterPresetCollection & printer_presets )
2020-07-21 14:21:18 +00:00
{
2020-07-28 09:31:16 +00:00
std : : vector < std : : string > presets ;
2020-07-21 14:21:18 +00:00
for ( const Preset & preset : printer_presets )
if ( has_print_host_information ( preset . config ) )
2020-07-28 09:31:16 +00:00
presets . emplace_back ( preset . name ) ;
2020-07-21 14:21:18 +00:00
2020-07-28 09:31:16 +00:00
return presets ;
2020-07-21 14:21:18 +00:00
}
bool PhysicalPrinter : : has_print_host_information ( const DynamicPrintConfig & config )
{
2020-10-28 08:51:05 +00:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 14:21:18 +00:00
if ( ! config . opt_string ( opt ) . empty ( ) )
return true ;
return false ;
}
2020-07-14 13:34:08 +00:00
const std : : set < std : : string > & PhysicalPrinter : : get_preset_names ( ) const
2020-06-26 07:58:39 +00:00
{
2020-07-14 13:34:08 +00:00
return preset_names ;
2020-06-26 07:58:39 +00:00
}
2021-02-24 08:22:31 +00:00
bool PhysicalPrinter : : has_empty_config ( ) const
2020-06-30 12:12:47 +00:00
{
2021-02-24 08:22:31 +00:00
return config . opt_string ( " print_host " ) . empty ( ) & &
config . opt_string ( " printhost_apikey " ) . empty ( ) & &
config . opt_string ( " printhost_cafile " ) . empty ( ) & &
2020-10-28 08:51:05 +00:00
config . opt_string ( " printhost_port " ) . empty ( ) & &
2021-02-24 08:22:31 +00:00
config . opt_string ( " printhost_user " ) . empty ( ) & &
2020-09-08 13:30:59 +00:00
config . opt_string ( " printhost_password " ) . empty ( ) ;
2020-06-30 12:12:47 +00:00
}
2021-07-20 13:22:52 +00:00
// temporary workaround for compatibility with older Slicer
static void update_preset_name_option ( const std : : set < std : : string > & preset_names , DynamicPrintConfig & config )
{
std : : string name ;
for ( auto el : preset_names )
name + = el + " ; " ;
name . pop_back ( ) ;
config . set_key_value ( " preset_name " , new ConfigOptionString ( name ) ) ;
}
2020-07-14 13:34:08 +00:00
void PhysicalPrinter : : update_preset_names_in_config ( )
{
if ( ! preset_names . empty ( ) ) {
2021-02-25 19:23:53 +00:00
std : : vector < std : : string > & values = config . option < ConfigOptionStrings > ( " preset_names " ) - > values ;
values . clear ( ) ;
for ( auto preset : preset_names )
values . push_back ( preset ) ;
2021-07-20 08:18:59 +00:00
// temporary workaround for compatibility with older Slicer
2021-07-20 13:22:52 +00:00
update_preset_name_option ( preset_names , config ) ;
2021-02-24 08:22:31 +00:00
}
2020-07-14 13:34:08 +00:00
}
void PhysicalPrinter : : save ( const std : : string & file_name_from , const std : : string & file_name_to )
{
// rename the file
boost : : nowide : : rename ( file_name_from . data ( ) , file_name_to . data ( ) ) ;
this - > file = file_name_to ;
// save configuration
this - > config . save ( this - > file ) ;
}
2020-06-24 06:50:01 +00:00
void PhysicalPrinter : : update_from_preset ( const Preset & preset )
{
2020-09-08 13:30:59 +00:00
config . apply_only ( preset . config , printer_options ( ) , true ) ;
2020-07-14 13:34:08 +00:00
// add preset names to the options list
2020-10-06 10:32:11 +00:00
preset_names . emplace ( preset . name ) ;
2020-07-14 13:34:08 +00:00
update_preset_names_in_config ( ) ;
2020-06-24 06:50:01 +00:00
}
void PhysicalPrinter : : update_from_config ( const DynamicPrintConfig & new_config )
{
config . apply_only ( new_config , printer_options ( ) , false ) ;
2020-07-14 13:34:08 +00:00
2021-02-25 19:23:53 +00:00
const std : : vector < std : : string > & values = config . option < ConfigOptionStrings > ( " preset_names " ) - > values ;
if ( values . empty ( ) )
preset_names . clear ( ) ;
2021-07-20 13:22:52 +00:00
else {
2020-07-14 13:34:08 +00:00
for ( const std : : string & val : values )
preset_names . emplace ( val ) ;
2021-07-20 13:22:52 +00:00
// temporary workaround for compatibility with older Slicer
update_preset_name_option ( preset_names , config ) ;
}
2020-06-24 06:50:01 +00:00
}
2020-07-14 13:34:08 +00:00
void PhysicalPrinter : : reset_presets ( )
{
return preset_names . clear ( ) ;
}
bool PhysicalPrinter : : add_preset ( const std : : string & preset_name )
{
return preset_names . emplace ( preset_name ) . second ;
}
2020-07-20 12:56:09 +00:00
bool PhysicalPrinter : : delete_preset ( const std : : string & preset_name )
{
return preset_names . erase ( preset_name ) > 0 ;
}
2021-02-24 08:22:31 +00:00
PhysicalPrinter : : PhysicalPrinter ( const std : : string & name , const DynamicPrintConfig & default_config ) :
2020-12-09 11:46:26 +00:00
name ( name ) , config ( default_config )
{
update_from_config ( config ) ;
}
2020-09-08 13:30:59 +00:00
PhysicalPrinter : : PhysicalPrinter ( const std : : string & name , const DynamicPrintConfig & default_config , const Preset & preset ) :
name ( name ) , config ( default_config )
2020-06-24 06:50:01 +00:00
{
update_from_preset ( preset ) ;
}
2020-06-26 07:58:39 +00:00
void PhysicalPrinter : : set_name ( const std : : string & name )
{
this - > name = name ;
}
2020-07-17 12:32:38 +00:00
std : : string PhysicalPrinter : : get_full_name ( std : : string preset_name ) const
2020-06-26 07:58:39 +00:00
{
2020-07-17 12:32:38 +00:00
return name + separator ( ) + preset_name ;
2020-06-26 07:58:39 +00:00
}
std : : string PhysicalPrinter : : get_short_name ( std : : string full_name )
{
2020-07-14 13:34:08 +00:00
int pos = full_name . find ( separator ( ) ) ;
2020-07-17 12:32:38 +00:00
if ( pos > 0 )
boost : : erase_tail ( full_name , full_name . length ( ) - pos ) ;
2020-06-26 07:58:39 +00:00
return full_name ;
}
2020-07-17 12:32:38 +00:00
std : : string PhysicalPrinter : : get_preset_name ( std : : string name )
2020-07-14 13:34:08 +00:00
{
2020-07-17 12:32:38 +00:00
int pos = name . find ( separator ( ) ) ;
boost : : erase_head ( name , pos + 3 ) ;
return Preset : : remove_suffix_modified ( name ) ;
2020-07-14 13:34:08 +00:00
}
2020-06-24 06:50:01 +00:00
// -----------------------------------
// *** PhysicalPrinterCollection ***
// -----------------------------------
PhysicalPrinterCollection : : PhysicalPrinterCollection ( const std : : vector < std : : string > & keys )
{
2020-09-08 13:30:59 +00:00
// Default config for a physical printer containing all key/value pairs of PhysicalPrinter::printer_options().
for ( const std : : string & key : keys ) {
const ConfigOptionDef * opt = print_config_def . get ( key ) ;
assert ( opt ) ;
assert ( opt - > default_value ) ;
m_default_config . set_key_value ( key , opt - > default_value - > clone ( ) ) ;
}
2020-06-24 06:50:01 +00:00
}
2020-07-20 14:27:39 +00:00
// Load all printers found in dir_path.
2020-06-24 06:50:01 +00:00
// Throws an exception on error.
Support for forward compatibility of configurations, user and system
config bundles, project files (3MFs, AMFs). When loading these files,
the caller may decide whether to substitute some of the configuration
values the current PrusaSlicer version does not understand with
some reasonable default value, and whether to report it. If substitution
is disabled, an exception is being thrown as before this commit.
If substitution is enabled, list of substitutions is returned by the
API to be presented to the user. This allows us to introduce for example
new firmware flavor key in PrusaSlicer 2.4 while letting PrusaSlicer
2.3.2 to fall back to some default and to report it to the user.
When slicing from command line, substutions are performed by default
and reported into the console, however substitutions may be either
disabled or made silent with the new "config-compatibility" command
line option.
Substitute enums and bools only. Allow booleans to be parsed as
true: "1", "enabled", "on" case insensitive
false: "0", "disabled", "off" case insensitive
This will allow us in the future for example to switch the draft_shield
boolean to an enum with the following values: "disabled" / "enabled" / "limited".
Added "enum_bitmask.hpp" - support for type safe sets of options.
See for example PresetBundle::load_configbundle(...
LoadConfigBundleAttributes flags) for an example of intended usage.
WIP: GUI for reporting the list of config substitutions needs to be
implemented by @YuSanka.
2021-06-27 14:04:23 +00:00
void PhysicalPrinterCollection : : load_printers (
const std : : string & dir_path , const std : : string & subdir ,
PresetsConfigSubstitutions & substitutions , ForwardCompatibilitySubstitutionRule substitution_rule )
2020-06-24 06:50:01 +00:00
{
2021-02-24 08:22:31 +00:00
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
2020-09-01 12:15:19 +00:00
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost : : filesystem : : path dir = boost : : filesystem : : absolute ( boost : : filesystem : : path ( dir_path ) / subdir ) . make_preferred ( ) ;
2020-06-24 06:50:01 +00:00
m_dir_path = dir . string ( ) ;
std : : string errors_cummulative ;
// Store the loaded printers into a new vector, otherwise the binary search for already existing presets would be broken.
std : : deque < PhysicalPrinter > printers_loaded ;
for ( auto & dir_entry : boost : : filesystem : : directory_iterator ( dir ) )
if ( Slic3r : : is_ini_file ( dir_entry ) ) {
std : : string name = dir_entry . path ( ) . filename ( ) . string ( ) ;
// Remove the .ini suffix.
name . erase ( name . size ( ) - 4 ) ;
if ( this - > find_printer ( name , false ) ) {
// This happens when there's is a preset (most likely legacy one) with the same name as a system preset
// that's already been loaded from a bundle.
2020-07-20 14:27:39 +00:00
BOOST_LOG_TRIVIAL ( warning ) < < " Printer already present, not loading: " < < name ;
2020-06-24 06:50:01 +00:00
continue ;
}
try {
2020-09-08 13:30:59 +00:00
PhysicalPrinter printer ( name , this - > default_config ( ) ) ;
2020-06-24 06:50:01 +00:00
printer . file = dir_entry . path ( ) . string ( ) ;
// Load the preset file, apply preset values on top of defaults.
try {
DynamicPrintConfig config ;
Support for forward compatibility of configurations, user and system
config bundles, project files (3MFs, AMFs). When loading these files,
the caller may decide whether to substitute some of the configuration
values the current PrusaSlicer version does not understand with
some reasonable default value, and whether to report it. If substitution
is disabled, an exception is being thrown as before this commit.
If substitution is enabled, list of substitutions is returned by the
API to be presented to the user. This allows us to introduce for example
new firmware flavor key in PrusaSlicer 2.4 while letting PrusaSlicer
2.3.2 to fall back to some default and to report it to the user.
When slicing from command line, substutions are performed by default
and reported into the console, however substitutions may be either
disabled or made silent with the new "config-compatibility" command
line option.
Substitute enums and bools only. Allow booleans to be parsed as
true: "1", "enabled", "on" case insensitive
false: "0", "disabled", "off" case insensitive
This will allow us in the future for example to switch the draft_shield
boolean to an enum with the following values: "disabled" / "enabled" / "limited".
Added "enum_bitmask.hpp" - support for type safe sets of options.
See for example PresetBundle::load_configbundle(...
LoadConfigBundleAttributes flags) for an example of intended usage.
WIP: GUI for reporting the list of config substitutions needs to be
implemented by @YuSanka.
2021-06-27 14:04:23 +00:00
ConfigSubstitutions config_substitutions = config . load_from_ini ( printer . file , substitution_rule ) ;
if ( ! config_substitutions . empty ( ) )
substitutions . push_back ( { name , Preset : : TYPE_PHYSICAL_PRINTER , PresetConfigSubstitutions : : Source : : UserFile , printer . file , std : : move ( config_substitutions ) } ) ;
2020-06-24 06:50:01 +00:00
printer . update_from_config ( config ) ;
printer . loaded = true ;
}
catch ( const std : : ifstream : : failure & err ) {
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( std : : string ( " The selected preset cannot be loaded: " ) + printer . file + " \n \t Reason: " + err . what ( ) ) ;
2020-06-24 06:50:01 +00:00
}
catch ( const std : : runtime_error & err ) {
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( std : : string ( " Failed loading the preset file: " ) + printer . file + " \n \t Reason: " + err . what ( ) ) ;
2020-06-24 06:50:01 +00:00
}
printers_loaded . emplace_back ( printer ) ;
}
catch ( const std : : runtime_error & err ) {
errors_cummulative + = err . what ( ) ;
errors_cummulative + = " \n " ;
}
2020-06-16 10:57:49 +00:00
}
2020-06-24 06:50:01 +00:00
m_printers . insert ( m_printers . end ( ) , std : : make_move_iterator ( printers_loaded . begin ( ) ) , std : : make_move_iterator ( printers_loaded . end ( ) ) ) ;
std : : sort ( m_printers . begin ( ) , m_printers . end ( ) ) ;
if ( ! errors_cummulative . empty ( ) )
2020-09-14 14:27:55 +00:00
throw Slic3r : : RuntimeError ( errors_cummulative ) ;
2020-06-24 06:50:01 +00:00
}
2020-06-16 10:57:49 +00:00
2020-10-01 20:48:00 +00:00
void PhysicalPrinterCollection : : load_printer ( const std : : string & path , const std : : string & name , DynamicPrintConfig & & config , bool select , bool save /* = false*/ )
{
auto it = this - > find_printer_internal ( name ) ;
if ( it = = m_printers . end ( ) | | it - > name ! = name ) {
// The preset was not found. Create a new preset.
it = m_printers . emplace ( it , PhysicalPrinter ( name , config ) ) ;
}
it - > file = path ;
it - > config = std : : move ( config ) ;
it - > loaded = true ;
if ( select )
this - > select_printer ( * it ) ;
if ( save )
it - > save ( ) ;
}
2020-07-20 14:27:39 +00:00
// if there is saved user presets, contains information about "Print Host upload",
// Create default printers with this presets
2020-07-21 14:21:18 +00:00
// Note! "Print Host upload" options will be cleared after physical printer creations
2020-07-28 09:31:16 +00:00
void PhysicalPrinterCollection : : load_printers_from_presets ( PrinterPresetCollection & printer_presets )
2020-07-20 14:27:39 +00:00
{
int cnt = 0 ;
2020-07-21 14:21:18 +00:00
for ( Preset & preset : printer_presets ) {
DynamicPrintConfig & config = preset . config ;
2020-10-28 08:51:05 +00:00
for ( const char * option : legacy_print_host_options ) {
2020-07-21 14:21:18 +00:00
if ( ! config . opt_string ( option ) . empty ( ) ) {
// check if printer with those "Print Host upload" options already exist
PhysicalPrinter * existed_printer = find_printer_with_same_config ( config ) ;
if ( existed_printer )
// just add preset for this printer
existed_printer - > add_preset ( preset . name ) ;
else {
2020-07-28 09:31:16 +00:00
std : : string new_printer_name = ( boost : : format ( " Printer %1% " ) % + + cnt ) . str ( ) ;
while ( find_printer ( new_printer_name ) )
new_printer_name = ( boost : : format ( " Printer %1% " ) % + + cnt ) . str ( ) ;
2020-07-21 14:21:18 +00:00
// create new printer from this preset
2020-09-08 13:30:59 +00:00
PhysicalPrinter printer ( new_printer_name , this - > default_config ( ) , preset ) ;
2020-07-21 14:21:18 +00:00
printer . loaded = true ;
save_printer ( printer ) ;
}
// erase "Print Host upload" information from the preset
2020-10-28 08:51:05 +00:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 14:21:18 +00:00
config . opt_string ( opt ) . clear ( ) ;
// save changes for preset
preset . save ( ) ;
// update those changes for edited preset if it's equal to the preset
Preset & edited = printer_presets . get_edited_preset ( ) ;
if ( preset . name = = edited . name ) {
2020-10-28 08:51:05 +00:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 14:21:18 +00:00
edited . config . opt_string ( opt ) . clear ( ) ;
}
break ;
}
2020-07-20 14:27:39 +00:00
}
}
}
2020-10-01 20:48:00 +00:00
PhysicalPrinter * PhysicalPrinterCollection : : find_printer ( const std : : string & name , bool case_sensitive_search )
2020-06-24 06:50:01 +00:00
{
2020-10-01 20:48:00 +00:00
auto it = this - > find_printer_internal ( name , case_sensitive_search ) ;
2020-06-24 06:50:01 +00:00
// Ensure that a temporary copy is returned if the preset found is currently selected.
2020-10-01 20:48:00 +00:00
auto is_equal_name = [ name , case_sensitive_search ] ( const std : : string & in_name ) {
if ( case_sensitive_search )
return in_name = = name ;
return boost : : to_lower_copy < std : : string > ( in_name ) = = boost : : to_lower_copy < std : : string > ( name ) ;
} ;
if ( it = = m_printers . end ( ) | | ! is_equal_name ( it - > name ) )
return nullptr ;
return & this - > printer ( it - m_printers . begin ( ) ) ;
}
std : : deque < PhysicalPrinter > : : iterator PhysicalPrinterCollection : : find_printer_internal ( const std : : string & name , bool case_sensitive_search /* = true*/ )
{
if ( case_sensitive_search )
return Slic3r : : lower_bound_by_predicate ( m_printers . begin ( ) , m_printers . end ( ) , [ & name ] ( const auto & l ) { return l . name < name ; } ) ;
std : : string low_name = boost : : to_lower_copy < std : : string > ( name ) ;
2020-10-06 10:32:11 +00:00
size_t i = 0 ;
2020-10-01 20:48:00 +00:00
for ( const PhysicalPrinter & printer : m_printers ) {
if ( boost : : to_lower_copy < std : : string > ( printer . name ) = = low_name )
break ;
i + + ;
}
if ( i = = m_printers . size ( ) )
return m_printers . end ( ) ;
return m_printers . begin ( ) + i ;
2020-06-16 10:57:49 +00:00
}
2019-04-26 14:53:04 +00:00
2020-07-21 14:21:18 +00:00
PhysicalPrinter * PhysicalPrinterCollection : : find_printer_with_same_config ( const DynamicPrintConfig & config )
{
for ( const PhysicalPrinter & printer : * this ) {
bool is_equal = true ;
2020-10-28 08:51:05 +00:00
for ( const char * opt : legacy_print_host_options )
2020-07-21 14:21:18 +00:00
if ( is_equal & & printer . config . opt_string ( opt ) ! = config . opt_string ( opt ) )
is_equal = false ;
2021-02-24 08:22:31 +00:00
2020-07-21 14:21:18 +00:00
if ( is_equal )
return find_printer ( printer . name ) ;
}
return nullptr ;
}
2020-06-24 06:50:01 +00:00
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std : : string PhysicalPrinterCollection : : path_from_name ( const std : : string & new_name ) const
{
std : : string file_name = boost : : iends_with ( new_name , " .ini " ) ? new_name : ( new_name + " .ini " ) ;
return ( boost : : filesystem : : path ( m_dir_path ) / file_name ) . make_preferred ( ) . string ( ) ;
}
2020-07-20 12:56:09 +00:00
void PhysicalPrinterCollection : : save_printer ( PhysicalPrinter & edited_printer , const std : : string & renamed_from /* = ""*/ )
2020-06-16 10:57:49 +00:00
{
2021-02-24 08:22:31 +00:00
// controll and update preset_names in edited_printer config
2020-07-20 12:56:09 +00:00
edited_printer . update_preset_names_in_config ( ) ;
2020-07-14 13:34:08 +00:00
std : : string name = renamed_from . empty ( ) ? edited_printer . name : renamed_from ;
2020-06-16 10:57:49 +00:00
// 1) Find the printer with a new_name or create a new one,
// initialize it with the edited config.
2020-07-14 13:34:08 +00:00
auto it = this - > find_printer_internal ( name ) ;
if ( it ! = m_printers . end ( ) & & it - > name = = name ) {
2020-06-24 06:50:01 +00:00
// Printer with the same name found.
2020-06-16 10:57:49 +00:00
// Overwriting an existing preset.
2020-06-24 06:50:01 +00:00
it - > config = std : : move ( edited_printer . config ) ;
2020-07-14 13:34:08 +00:00
it - > name = edited_printer . name ;
it - > preset_names = edited_printer . preset_names ;
2020-10-01 20:48:00 +00:00
// sort printers and get new it
std : : sort ( m_printers . begin ( ) , m_printers . end ( ) ) ;
it = this - > find_printer_internal ( edited_printer . name ) ;
2020-06-16 10:57:49 +00:00
}
else {
// Creating a new printer.
2020-10-01 20:48:00 +00:00
it = m_printers . emplace ( it , edited_printer ) ;
2020-06-16 10:57:49 +00:00
}
2020-06-24 06:50:01 +00:00
assert ( it ! = m_printers . end ( ) ) ;
// 2) Save printer
PhysicalPrinter & printer = * it ;
if ( printer . file . empty ( ) )
printer . file = this - > path_from_name ( printer . name ) ;
2020-07-14 13:34:08 +00:00
if ( printer . file = = this - > path_from_name ( printer . name ) )
printer . save ( ) ;
else
// if printer was renamed, we should rename a file and than save the config
printer . save ( printer . file , this - > path_from_name ( printer . name ) ) ;
2020-06-24 06:50:01 +00:00
// update idx_selected
m_idx_selected = it - m_printers . begin ( ) ;
}
bool PhysicalPrinterCollection : : delete_printer ( const std : : string & name )
{
auto it = this - > find_printer_internal ( name ) ;
2020-06-24 10:28:00 +00:00
if ( it = = m_printers . end ( ) )
return false ;
2020-06-24 06:50:01 +00:00
const PhysicalPrinter & printer = * it ;
// Erase the preset file.
boost : : nowide : : remove ( printer . file . c_str ( ) ) ;
m_printers . erase ( it ) ;
return true ;
2020-06-16 10:57:49 +00:00
}
2020-06-24 06:50:01 +00:00
2020-06-24 10:28:00 +00:00
bool PhysicalPrinterCollection : : delete_selected_printer ( )
{
if ( ! has_selection ( ) )
return false ;
const PhysicalPrinter & printer = this - > get_selected_printer ( ) ;
// Erase the preset file.
boost : : nowide : : remove ( printer . file . c_str ( ) ) ;
// Remove the preset from the list.
m_printers . erase ( m_printers . begin ( ) + m_idx_selected ) ;
// unselect all printers
unselect_printer ( ) ;
return true ;
}
2020-07-28 09:31:16 +00:00
bool PhysicalPrinterCollection : : delete_preset_from_printers ( const std : : string & preset_name )
2020-07-20 12:56:09 +00:00
{
std : : vector < std : : string > printers_for_delete ;
2020-07-28 09:31:16 +00:00
for ( PhysicalPrinter & printer : m_printers ) {
2020-07-20 12:56:09 +00:00
if ( printer . preset_names . size ( ) = = 1 & & * printer . preset_names . begin ( ) = = preset_name )
printers_for_delete . emplace_back ( printer . name ) ;
2020-07-28 09:31:16 +00:00
else if ( printer . delete_preset ( preset_name ) )
2020-07-20 12:56:09 +00:00
save_printer ( printer ) ;
2020-07-28 09:31:16 +00:00
}
2020-07-20 12:56:09 +00:00
2020-07-28 09:31:16 +00:00
if ( ! printers_for_delete . empty ( ) )
2020-07-20 12:56:09 +00:00
for ( const std : : string & printer_name : printers_for_delete )
delete_printer ( printer_name ) ;
2020-07-28 09:31:16 +00:00
unselect_printer ( ) ;
2020-07-20 12:56:09 +00:00
return true ;
}
2021-02-25 19:23:53 +00:00
// Get list of printers which have more than one preset and "preset_names" preset is one of them
2020-07-28 09:31:16 +00:00
std : : vector < std : : string > PhysicalPrinterCollection : : get_printers_with_preset ( const std : : string & preset_name )
{
std : : vector < std : : string > printers ;
for ( auto printer : m_printers ) {
if ( printer . preset_names . size ( ) = = 1 )
2021-02-24 08:22:31 +00:00
continue ;
2020-07-28 09:31:16 +00:00
if ( printer . preset_names . find ( preset_name ) ! = printer . preset_names . end ( ) )
printers . emplace_back ( printer . name ) ;
}
return printers ;
}
2021-02-25 19:23:53 +00:00
// Get list of printers which has only "preset_names" preset
2020-07-28 09:31:16 +00:00
std : : vector < std : : string > PhysicalPrinterCollection : : get_printers_with_only_preset ( const std : : string & preset_name )
{
std : : vector < std : : string > printers ;
for ( auto printer : m_printers )
if ( printer . preset_names . size ( ) = = 1 & & * printer . preset_names . begin ( ) = = preset_name )
printers . emplace_back ( printer . name ) ;
return printers ;
}
2020-07-17 12:32:38 +00:00
std : : string PhysicalPrinterCollection : : get_selected_full_printer_name ( ) const
2020-06-24 06:50:01 +00:00
{
2020-07-17 12:32:38 +00:00
return ( m_idx_selected = = size_t ( - 1 ) ) ? std : : string ( ) : this - > get_selected_printer ( ) . get_full_name ( m_selected_preset ) ;
}
2020-07-23 10:17:18 +00:00
void PhysicalPrinterCollection : : select_printer ( const std : : string & full_name )
2020-07-17 12:32:38 +00:00
{
std : : string printer_name = PhysicalPrinter : : get_short_name ( full_name ) ;
auto it = this - > find_printer_internal ( printer_name ) ;
2020-07-23 10:17:18 +00:00
if ( it = = m_printers . end ( ) ) {
unselect_printer ( ) ;
return ;
}
2020-06-24 06:50:01 +00:00
// update idx_selected
2020-07-23 10:17:18 +00:00
m_idx_selected = it - m_printers . begin ( ) ;
2020-07-20 12:56:09 +00:00
// update name of the currently selected preset
2020-07-23 10:17:18 +00:00
if ( printer_name = = full_name )
// use first preset in the list
m_selected_preset = * it - > preset_names . begin ( ) ;
else
m_selected_preset = it - > get_preset_name ( full_name ) ;
2020-07-20 12:56:09 +00:00
}
2020-12-15 11:09:03 +00:00
void PhysicalPrinterCollection : : select_printer ( const std : : string & printer_name , const std : : string & preset_name )
{
if ( preset_name . empty ( ) )
return select_printer ( printer_name ) ;
return select_printer ( printer_name + PhysicalPrinter : : separator ( ) + preset_name ) ;
}
2020-07-23 10:17:18 +00:00
void PhysicalPrinterCollection : : select_printer ( const PhysicalPrinter & printer )
2020-07-20 12:56:09 +00:00
{
return select_printer ( printer . name ) ;
}
2020-07-17 12:32:38 +00:00
bool PhysicalPrinterCollection : : has_selection ( ) const
{
return m_idx_selected ! = size_t ( - 1 ) ;
}
void PhysicalPrinterCollection : : unselect_printer ( )
{
m_idx_selected = size_t ( - 1 ) ;
m_selected_preset . clear ( ) ;
}
bool PhysicalPrinterCollection : : is_selected ( PhysicalPrinterCollection : : ConstIterator it , const std : : string & preset_name ) const
{
2021-02-24 08:22:31 +00:00
return m_idx_selected = = size_t ( it - m_printers . begin ( ) ) & &
2020-07-17 12:32:38 +00:00
m_selected_preset = = preset_name ;
}
2020-06-24 06:50:01 +00:00
2020-01-13 15:01:01 +00:00
namespace PresetUtils {
const VendorProfile : : PrinterModel * system_printer_model ( const Preset & preset )
{
const VendorProfile : : PrinterModel * out = nullptr ;
if ( preset . vendor ! = nullptr ) {
auto * printer_model = preset . config . opt < ConfigOptionString > ( " printer_model " ) ;
if ( printer_model ! = nullptr & & ! printer_model - > value . empty ( ) ) {
auto it = std : : find_if ( preset . vendor - > models . begin ( ) , preset . vendor - > models . end ( ) , [ printer_model ] ( const VendorProfile : : PrinterModel & pm ) { return pm . id = = printer_model - > value ; } ) ;
if ( it ! = preset . vendor - > models . end ( ) )
out = & ( * it ) ;
}
}
return out ;
}
2020-08-21 08:59:07 +00:00
std : : string system_printer_bed_model ( const Preset & preset )
{
std : : string out ;
const VendorProfile : : PrinterModel * pm = PresetUtils : : system_printer_model ( preset ) ;
2020-11-19 18:02:32 +00:00
if ( pm ! = nullptr & & ! pm - > bed_model . empty ( ) ) {
out = Slic3r : : data_dir ( ) + " /vendor/ " + preset . vendor - > id + " / " + pm - > bed_model ;
if ( ! boost : : filesystem : : exists ( boost : : filesystem : : path ( out ) ) )
out = Slic3r : : resources_dir ( ) + " /profiles/ " + preset . vendor - > id + " / " + pm - > bed_model ;
}
2020-08-21 08:59:07 +00:00
return out ;
}
std : : string system_printer_bed_texture ( const Preset & preset )
{
std : : string out ;
const VendorProfile : : PrinterModel * pm = PresetUtils : : system_printer_model ( preset ) ;
2020-11-19 18:02:32 +00:00
if ( pm ! = nullptr & & ! pm - > bed_texture . empty ( ) ) {
out = Slic3r : : data_dir ( ) + " /vendor/ " + preset . vendor - > id + " / " + pm - > bed_texture ;
if ( ! boost : : filesystem : : exists ( boost : : filesystem : : path ( out ) ) )
out = Slic3r : : resources_dir ( ) + " /profiles/ " + preset . vendor - > id + " / " + pm - > bed_texture ;
}
2020-08-21 08:59:07 +00:00
return out ;
}
2020-01-13 15:01:01 +00:00
} // namespace PresetUtils
2017-09-19 11:55:48 +00:00
} // namespace Slic3r