diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 59477db4a..250c1c968 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -540,6 +540,15 @@ void Model::reset_auto_extruder_id() s_auto_extruder_id = 1; } +std::string Model::propose_export_file_name() const +{ + for (const ModelObject *model_object : this->objects) + for (ModelInstance *model_instance : model_object->instances) + if (model_instance->is_printable()) + return model_object->input_file; + return std::string(); +} + ModelObject::~ModelObject() { this->clear_volumes(); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b9831336c..b02862203 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -653,6 +653,9 @@ public: static std::string get_auto_extruder_id_as_string(unsigned int max_extruders); static void reset_auto_extruder_id(); + // Propose an output file name based on the first printable object's name. + std::string propose_export_file_name() const; + private: MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model) }; diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 198872b1d..d2be6e9f6 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -94,6 +94,36 @@ void PlaceholderParser::update_timestamp(DynamicConfig &config) config.set_key_value("second", new ConfigOptionInt(timeinfo->tm_sec)); } +// Ignore this key by the placeholder parser. +static inline bool placeholder_parser_ignore(const ConfigDef *def, const std::string &opt_key) +{ + const ConfigOptionDef *opt_def = def->get(opt_key); + assert(opt_def != nullptr); + return (opt_def->multiline && boost::ends_with(opt_key, "_gcode")) || opt_key == "post_process"; +} + +static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConfig &config_new, const std::string &opt_key) +{ + const ConfigOption *opt_old = config_old.option(opt_key); + const ConfigOption *opt_new = config_new.option(opt_key); + assert(opt_new != nullptr); + if (opt_old == nullptr) + return false; + return (opt_new->type() == coFloatOrPercent) ? + dynamic_cast(opt_old)->value == config_new.get_abs_value(opt_key) : + *opt_new == *opt_old; +} + +std::vector PlaceholderParser::config_diff(const DynamicPrintConfig &rhs) +{ + const ConfigDef *def = rhs.def(); + std::vector diff_keys; + for (const t_config_option_key &opt_key : rhs.keys()) + if (! placeholder_parser_ignore(def, opt_key) && ! opts_equal(m_config, rhs, opt_key)) + diff_keys.emplace_back(opt_key); + return diff_keys; +} + // Scalar configuration values are stored into m_single, // vector configuration values are stored into m_multiple. // All vector configuration values stored into the PlaceholderParser @@ -105,28 +135,39 @@ bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) const ConfigDef *def = rhs.def(); bool modified = false; for (const t_config_option_key &opt_key : rhs.keys()) { - const ConfigOptionDef *opt_def = def->get(opt_key); - if ((opt_def->multiline && boost::ends_with(opt_key, "_gcode")) || opt_key == "post_process") + if (placeholder_parser_ignore(def, opt_key)) continue; - const ConfigOption *opt_rhs = rhs.option(opt_key); - const ConfigOption *opt_old = m_config.option(opt_key, false); - if (opt_old != nullptr) { - if (opt_rhs->type() == coFloatOrPercent ? - dynamic_cast(opt_old)->value == rhs.get_abs_value(opt_key) - : *opt_rhs == *opt_old) - // no need to update - continue; + if (! opts_equal(m_config, rhs, opt_key)) { + // Store a copy of the config option. + // Convert FloatOrPercent values to floats first. + //FIXME there are some ratio_over chains, which end with empty ratio_with. + // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. + const ConfigOption *opt_rhs = rhs.option(opt_key); + this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? + new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : + opt_rhs->clone()); + modified = true; } + } + return modified; +} + +void PlaceholderParser::apply_only(const DynamicPrintConfig &rhs, const std::vector &keys) +{ +#ifdef _DEBUG + const ConfigDef *def = rhs.def(); +#endif /* _DEBUG */ + for (const t_config_option_key &opt_key : keys) { + assert(! placeholder_parser_ignore(def, opt_key)); // Store a copy of the config option. // Convert FloatOrPercent values to floats first. //FIXME there are some ratio_over chains, which end with empty ratio_with. // For example, XXX_extrusion_width parameters are not handled by get_abs_value correctly. + const ConfigOption *opt_rhs = rhs.option(opt_key); this->set(opt_key, (opt_rhs->type() == coFloatOrPercent) ? new ConfigOptionFloat(rhs.get_abs_value(opt_key)) : opt_rhs->clone()); - modified = true; } - return modified; } void PlaceholderParser::apply_env_variables() diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index d833969fc..b5ed56fa1 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -14,8 +14,14 @@ class PlaceholderParser public: PlaceholderParser(); + // Return a list of keys, which should be changed in m_config from rhs. + // This contains keys, which are found in rhs, but not in m_config. + std::vector config_diff(const DynamicPrintConfig &rhs); // Return true if modified. bool apply_config(const DynamicPrintConfig &config); + // To be called on the values returned by PlaceholderParser::config_diff(). + // The keys should already be valid. + void apply_only(const DynamicPrintConfig &config, const std::vector &keys); void apply_env_variables(); // Add new ConfigOption values to m_config. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index dda4235b1..6bc953ede 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -442,7 +442,7 @@ bool Print::apply_config(DynamicPrintConfig config) config.normalize(); // apply variables to placeholder parser - m_placeholder_parser.apply_config(config); + this->placeholder_parser().apply_config(config); // handle changes to print config t_config_option_keys print_diff = m_config.diff(config); @@ -683,6 +683,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co t_config_option_keys print_diff = m_config.diff(config); t_config_option_keys object_diff = m_default_object_config.diff(config); t_config_option_keys region_diff = m_default_region_config.diff(config); + t_config_option_keys placeholder_parser_diff = this->placeholder_parser().config_diff(config); // Do not use the ApplyStatus as we will use the max function when updating apply_status. unsigned int apply_status = APPLY_STATUS_UNCHANGED; @@ -699,8 +700,15 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co update_apply_status(this->invalidate_state_by_config_options(print_diff)); // Apply variables to placeholder parser. The placeholder parser is used by G-code export, // which should be stopped if print_diff is not empty. - if (m_placeholder_parser.apply_config(config)) + if (! placeholder_parser_diff.empty()) { update_apply_status(this->invalidate_step(psGCodeExport)); + PlaceholderParser &pp = this->placeholder_parser(); + pp.apply_only(config, placeholder_parser_diff); + // Set the profile aliases for the PrintBase::output_filename() + pp.set("print_preset", config_in.option("print_settings_id" )->clone()); + pp.set("filament_preset", config_in.option("filament_settings_id")->clone()); + pp.set("printer_preset", config_in.option("printer_settings_id" )->clone()); + } // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. m_config.apply_only(config, print_diff, true); @@ -1113,6 +1121,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co if (! object->layer_height_profile_valid) object->update_layer_height_profile(); + //FIXME there may be a race condition with the G-code export running at the background thread. this->update_object_placeholders(); #ifdef _DEBUG @@ -1122,33 +1131,6 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co return static_cast(apply_status); } -// Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. -void Print::update_object_placeholders() -{ - // get the first input file name - std::string input_file; - std::vector v_scale; - for (const PrintObject *object : m_objects) { - const ModelObject &mobj = *object->model_object(); - // CHECK_ME -> Is the following correct ? - v_scale.push_back("x:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(X) * 100) + - "% y:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(Y) * 100) + - "% z:" + boost::lexical_cast(mobj.instances[0]->get_scaling_factor(Z) * 100) + "%"); - if (input_file.empty()) - input_file = mobj.input_file; - } - - PlaceholderParser &pp = m_placeholder_parser; - pp.set("scale", v_scale); - if (! input_file.empty()) { - // get basename with and without suffix - const std::string input_basename = boost::filesystem::path(input_file).filename().string(); - pp.set("input_filename", input_basename); - const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); - pp.set("input_filename_base", input_basename_base); - } -} - bool Print::has_infinite_skirt() const { return (m_config.skirt_height == -1 && m_config.skirts > 0) @@ -1851,60 +1833,6 @@ void Print::_make_wipe_tower() m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); } -std::string Print::output_filename() const -{ - DynamicConfig cfg_timestamp; - PlaceholderParser::update_timestamp(cfg_timestamp); - try { - return this->placeholder_parser().process(m_config.output_filename_format.value, 0, &cfg_timestamp); - } catch (std::runtime_error &err) { - throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); - } -} - -std::string Print::output_filepath(const std::string &path) const -{ - // if we were supplied no path, generate an automatic one based on our first object's input file - if (path.empty()) { - // get the first input file name - std::string input_file; - for (const PrintObject *object : m_objects) { - input_file = object->model_object()->input_file; - if (! input_file.empty()) - break; - } - return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).make_preferred().string(); - } - - // if we were supplied a directory, use it and append our automatically generated filename - boost::filesystem::path p(path); - if (boost::filesystem::is_directory(p)) - return (p / this->output_filename()).make_preferred().string(); - - // if we were supplied a file which is not a directory, use it - return path; -} - -void Print::export_png(const std::string &dirpath) -{ -// size_t idx = 0; -// for (PrintObject *obj : m_objects) { -// obj->slice(); -// this->set_status(int(floor(idx * 100. / m_objects.size() + 0.5)), "Slicing..."); -// ++ idx; -// } -// this->set_status(90, "Exporting zipped archive..."); -// print_to(*this, -// dirpath, -// float(m_config.bed_size_x.value), -// float(m_config.bed_size_y.value), -// int(m_config.pixel_width.value), -// int(m_config.pixel_height.value), -// float(m_config.exp_time.value), -// float(m_config.exp_time_first.value)); -// this->set_status(100, "Done."); -} - // Returns extruder this eec should be printed with, according to PrintRegion config int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion ®ion) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index b5c693ee4..4175d4f22 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -8,7 +8,6 @@ #include "Point.hpp" #include "Layer.hpp" #include "Model.hpp" -#include "PlaceholderParser.hpp" #include "Slicing.hpp" #include "GCode/ToolOrdering.hpp" #include "GCode/WipeTower.hpp" @@ -296,8 +295,6 @@ public: void process() override; void export_gcode(const std::string &path_template, GCodePreviewData *preview_data); - // SLA export, temporary. - void export_png(const std::string &dirpath); // methods for handling state bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); } @@ -330,8 +327,6 @@ public: PrintObject* get_object(size_t idx) { return m_objects[idx]; } const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } const PrintRegionPtrs& regions() const { return m_regions; } - const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } - PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } // How many of PrintObject::copies() over all print objects are there? // If zero, then the print is empty and the print shall not be executed. unsigned int num_object_instances() const; @@ -348,8 +343,8 @@ public: bool has_wipe_tower() const; const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; } - std::string output_filename() const; - std::string output_filepath(const std::string &path) const; + std::string output_filename() const override + { return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode"); } // Accessed by SupportMaterial const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } @@ -364,9 +359,6 @@ protected: bool invalidate_step(PrintStep step); private: - // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. - void update_object_placeholders(); - bool invalidate_state_by_config_options(const std::vector &opt_keys); void _make_skirt(); @@ -382,7 +374,6 @@ private: PrintRegionConfig m_default_region_config; PrintObjectPtrs m_objects; PrintRegionPtrs m_regions; - PlaceholderParser m_placeholder_parser; // Ordered collections of extrusion paths to build skirt loops and brim. ExtrusionEntityCollection m_skirt; diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index a44f42fbd..fb087612e 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -1,10 +1,91 @@ #include "PrintBase.hpp" +#include "I18N.hpp" + +//! macro used to mark string used at localization, +//! return same string +#define L(s) Slic3r::I18N::translate(s) + namespace Slic3r { size_t PrintStateBase::g_last_timestamp = 0; +// Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. +void PrintBase::update_object_placeholders() +{ + // get the first input file name + std::string input_file; + std::vector v_scale; + for (const ModelObject *model_object : m_model.objects) { + ModelInstance *printable = nullptr; + for (ModelInstance *model_instance : model_object->instances) + if (model_instance->is_printable()) { + printable = model_instance; + break; + } + if (printable) { + // CHECK_ME -> Is the following correct ? + v_scale.push_back("x:" + boost::lexical_cast(printable->get_scaling_factor(X) * 100) + + "% y:" + boost::lexical_cast(printable->get_scaling_factor(Y) * 100) + + "% z:" + boost::lexical_cast(printable->get_scaling_factor(Z) * 100) + "%"); + if (input_file.empty()) + input_file = model_object->input_file; + } + } + + PlaceholderParser &pp = m_placeholder_parser; + pp.set("scale", v_scale); + if (! input_file.empty()) { + // get basename with and without suffix + const std::string input_basename = boost::filesystem::path(input_file).filename().string(); + pp.set("input_filename", input_basename); + const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); + pp.set("input_filename_base", input_basename_base); + } +} + +std::string PrintBase::output_filename(const std::string &format, const std::string &default_ext) const +{ + DynamicConfig cfg_timestamp; + PlaceholderParser::update_timestamp(cfg_timestamp); + try { + boost::filesystem::path filename = this->placeholder_parser().process(format, 0, &cfg_timestamp); + if (filename.extension().empty()) + filename = boost::filesystem::change_extension(filename, default_ext); + return filename.string(); + } catch (std::runtime_error &err) { + throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what()); + } +} + +std::string PrintBase::output_filepath(const std::string &path) const +{ + // if we were supplied no path, generate an automatic one based on our first object's input file + if (path.empty()) { + // get the first input file name + std::string input_file; + for (const ModelObject *model_object : m_model.objects) { + for (ModelInstance *model_instance : model_object->instances) + if (model_instance->is_printable()) { + input_file = model_object->input_file; + break; + } + if (! input_file.empty()) + break; + } + return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).make_preferred().string(); + } + + // if we were supplied a directory, use it and append our automatically generated filename + boost::filesystem::path p(path); + if (boost::filesystem::is_directory(p)) + return (p / this->output_filename()).make_preferred().string(); + + // if we were supplied a file which is not a directory, use it + return path; +} + tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print) { return print->state_mutex(); diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index b61eedb9c..ca4d4eaf3 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -14,6 +14,7 @@ #include "tbb/mutex.h" #include "Model.hpp" +#include "PlaceholderParser.hpp" #include "PrintConfig.hpp" namespace Slic3r { @@ -266,7 +267,7 @@ public: // Various methods will call this callback to stop the background processing (the Print::process() call) // in case a successive change of the Print / PrintObject / PrintRegion instances changed // the state of the finished or running calculations. - void set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; } + void set_cancel_callback(cancel_callback_type cancel_callback) { m_cancel_callback = cancel_callback; } // Has the calculation been canceled? enum CancelStatus { // No cancelation, background processing should run. @@ -276,14 +277,20 @@ public: // Canceled internally from Print::apply() through the Print/PrintObject::invalidate_step() or ::invalidate_all_steps(). CANCELED_INTERNAL = 2 }; - CancelStatus cancel_status() const { return m_cancel_status; } + CancelStatus cancel_status() const { return m_cancel_status; } // Has the calculation been canceled? - bool canceled() const { return m_cancel_status != NOT_CANCELED; } + bool canceled() const { return m_cancel_status != NOT_CANCELED; } // Cancel the running computation. Stop execution of all the background threads. - void cancel() { m_cancel_status = CANCELED_BY_USER; } - void cancel_internal() { m_cancel_status = CANCELED_INTERNAL; } + void cancel() { m_cancel_status = CANCELED_BY_USER; } + void cancel_internal() { m_cancel_status = CANCELED_INTERNAL; } // Cancel the running computation. Stop execution of all the background threads. - void restart() { m_cancel_status = NOT_CANCELED; } + void restart() { m_cancel_status = NOT_CANCELED; } + + const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; } + PlaceholderParser& placeholder_parser() { return m_placeholder_parser; } + + virtual std::string output_filename() const = 0; + std::string output_filepath(const std::string &path) const; protected: friend class PrintObjectBase; @@ -297,6 +304,11 @@ protected: // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); } + // To be called by this->output_filename() with the format string pulled from the configuration layer. + std::string output_filename(const std::string &format, const std::string &default_ext) const; + // Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects. + void update_object_placeholders(); + Model m_model; private: @@ -311,6 +323,8 @@ private: // The mutex will be used to guard the worker thread against entering a stage // while the data influencing the stage is modified. mutable tbb::mutex m_state_mutex; + + PlaceholderParser m_placeholder_parser; }; template diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 77fc4dee4..430148618 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1322,7 +1322,7 @@ void PrintConfigDef::init_fff_params() def->cli = "output-filename-format=s"; def->full_width = true; def->mode = comExpert; - def->default_value = new ConfigOptionString("[input_filename_base].gcode"); + def->default_value = new ConfigOptionString("[input_filename_base]"); def = this->add("overhangs", coBool); def->label = L("Detect bridging perimeters"); @@ -2925,6 +2925,7 @@ StaticPrintConfig::StaticCache HostConfig::s_ca StaticPrintConfig::StaticCache FullPrintConfig::s_cache_FullPrintConfig; StaticPrintConfig::StaticCache SLAMaterialConfig::s_cache_SLAMaterialConfig; +StaticPrintConfig::StaticCache SLAPrintConfig::s_cache_SLAPrintConfig; StaticPrintConfig::StaticCache SLAPrintObjectConfig::s_cache_SLAPrintObjectConfig; StaticPrintConfig::StaticCache SLAPrinterConfig::s_cache_SLAPrinterConfig; StaticPrintConfig::StaticCache SLAFullPrintConfig::s_cache_SLAFullPrintConfig; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c51a7ae88..dac2de7cc 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -898,6 +898,20 @@ protected: } }; +// This object is mapped to Perl as Slic3r::Config::PrintRegion. +class SLAPrintConfig : public StaticPrintConfig +{ + STATIC_PRINT_CONFIG_CACHE(SLAPrintConfig) +public: + ConfigOptionString output_filename_format; + +protected: + void initialize(StaticCacheBase &cache, const char *base_ptr) + { + OPT_PTR(output_filename_format); + } +}; + class SLAPrintObjectConfig : public StaticPrintConfig { STATIC_PRINT_CONFIG_CACHE(SLAPrintObjectConfig) @@ -1028,10 +1042,10 @@ protected: } }; -class SLAFullPrintConfig : public SLAPrinterConfig, public SLAPrintObjectConfig, public SLAMaterialConfig +class SLAFullPrintConfig : public SLAPrinterConfig, public SLAPrintConfig, public SLAPrintObjectConfig, public SLAMaterialConfig { STATIC_PRINT_CONFIG_CACHE_DERIVED(SLAFullPrintConfig) - SLAFullPrintConfig() : SLAPrinterConfig(0), SLAPrintObjectConfig(0), SLAMaterialConfig(0) { initialize_cache(); *this = s_cache_SLAFullPrintConfig.defaults(); } + SLAFullPrintConfig() : SLAPrinterConfig(0), SLAPrintConfig(0), SLAPrintObjectConfig(0), SLAMaterialConfig(0) { initialize_cache(); *this = s_cache_SLAFullPrintConfig.defaults(); } public: // Validate the SLAFullPrintConfig. Returns an empty string on success, otherwise an error message is returned. @@ -1039,10 +1053,11 @@ public: protected: // Protected constructor to be called to initialize ConfigCache::m_default. - SLAFullPrintConfig(int) : SLAPrinterConfig(0), SLAPrintObjectConfig(0), SLAMaterialConfig(0) {} + SLAFullPrintConfig(int) : SLAPrinterConfig(0), SLAPrintConfig(0), SLAPrintObjectConfig(0), SLAMaterialConfig(0) {} void initialize(StaticCacheBase &cache, const char *base_ptr) { this->SLAPrinterConfig ::initialize(cache, base_ptr); + this->SLAPrintConfig ::initialize(cache, base_ptr); this->SLAPrintObjectConfig::initialize(cache, base_ptr); this->SLAMaterialConfig ::initialize(cache, base_ptr); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 6721decf9..8cc845472 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -107,8 +107,7 @@ static std::vector sla_instances(const ModelObject &mo return instances; } -SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, - const DynamicPrintConfig &config_in) +SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConfig &config_in) { #ifdef _DEBUG check_model_ids_validity(model); @@ -118,27 +117,44 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig config(config_in); config.normalize(); // Collect changes to print config. + t_config_option_keys print_diff = m_print_config.diff(config); t_config_option_keys printer_diff = m_printer_config.diff(config); t_config_option_keys material_diff = m_material_config.diff(config); t_config_option_keys object_diff = m_default_object_config.diff(config); + t_config_option_keys placeholder_parser_diff = this->placeholder_parser().config_diff(config); // Do not use the ApplyStatus as we will use the max function when updating apply_status. unsigned int apply_status = APPLY_STATUS_UNCHANGED; auto update_apply_status = [&apply_status](bool invalidated) { apply_status = std::max(apply_status, invalidated ? APPLY_STATUS_INVALIDATED : APPLY_STATUS_CHANGED); }; - if (! (printer_diff.empty() && material_diff.empty() && object_diff.empty())) + if (! (print_diff.empty() && printer_diff.empty() && material_diff.empty() && object_diff.empty())) update_apply_status(false); // Grab the lock for the Print / PrintObject milestones. tbb::mutex::scoped_lock lock(this->state_mutex()); // The following call may stop the background processing. + if (! print_diff.empty()) + update_apply_status(this->invalidate_state_by_config_options(print_diff)); if (! printer_diff.empty()) update_apply_status(this->invalidate_state_by_config_options(printer_diff)); if (! material_diff.empty()) update_apply_status(this->invalidate_state_by_config_options(material_diff)); + // Apply variables to placeholder parser. The placeholder parser is currently used + // only to generate the output file name. + if (! placeholder_parser_diff.empty()) { + // update_apply_status(this->invalidate_step(slapsRasterize)); + PlaceholderParser &pp = this->placeholder_parser(); + pp.apply_config(config); + // Set the profile aliases for the PrintBase::output_filename() + pp.set("print_preset", config_in.option("sla_print_settings_id")->clone()); + pp.set("material_preset", config_in.option("sla_material_settings_id")->clone()); + pp.set("printer_preset", config_in.option("printer_settings_id")->clone()); + } + // It is also safe to change m_config now after this->invalidate_state_by_config_options() call. + m_print_config.apply_only(config, print_diff, true); m_printer_config.apply_only(config, printer_diff, true); // Handle changes to material config. m_material_config.apply_only(config, material_diff, true); @@ -368,6 +384,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, update_apply_status(new_objects); } + this->update_object_placeholders(); + #ifdef _DEBUG check_model_ids_equal(m_model, model); #endif /* _DEBUG */ @@ -874,6 +892,7 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector steps; @@ -927,7 +946,9 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector steps; bool invalidated = false; for (const t_config_option_key &opt_key : opt_keys) { - if ( opt_key == "supports_enable" + if (opt_key == "layer_height") { + steps.emplace_back(slaposObjectSlice); + } else if (opt_key == "supports_enable" || opt_key == "support_head_front_diameter" || opt_key == "support_head_penetration" || opt_key == "support_head_width" diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index dbf3a49c7..ebdc3f854 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -191,6 +191,9 @@ public: } const PrintObjects& objects() const { return m_objects; } + std::string output_filename() const override + { return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip"); } + private: using SLAPrinter = FilePrinter; using SLAPrinterPtr = std::unique_ptr; @@ -198,6 +201,7 @@ private: // Invalidate steps based on a set of parameters changed. bool invalidate_state_by_config_options(const std::vector &opt_keys); + SLAPrintConfig m_print_config; SLAPrinterConfig m_printer_config; SLAMaterialConfig m_material_config; SLAPrintObjectConfig m_default_object_config; diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 625000fdb..6ff46e1ab 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -213,7 +213,7 @@ int main(int argc, char **argv) model.center_instances_around_point(cli_config.print_center); } if (outfile.empty()) - outfile = model.objects.front()->input_file + ".gcode"; + outfile = model.propose_export_file_name() + ".gcode"; for (auto* mo : model.objects) print.auto_assign_extruders(mo); print_config.normalize(); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 342c02e21..c41f2e3bd 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -402,6 +402,8 @@ const Transform3f& GLVolume::world_matrix() const const BoundingBoxf3& GLVolume::transformed_bounding_box() const { + assert(bounding_box.defined || bounding_box.min(0) >= bounding_box.max(0) || bounding_box.min(1) >= bounding_box.max(1) || bounding_box.min(2) >= bounding_box.max(2)); + if (m_transformed_bounding_box_dirty) { #if ENABLE_MODELVOLUME_TRANSFORM diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index d848cd1a7..1e8258d6f 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -40,8 +40,9 @@ BackgroundSlicingProcess::~BackgroundSlicingProcess() boost::nowide::remove(m_temp_output_path.c_str()); } -void BackgroundSlicingProcess::select_technology(PrinterTechnology tech) +bool BackgroundSlicingProcess::select_technology(PrinterTechnology tech) { + bool changed = false; if (m_print == nullptr || m_print->technology() != tech) { if (m_print != nullptr) this->reset(); @@ -49,8 +50,10 @@ void BackgroundSlicingProcess::select_technology(PrinterTechnology tech) case ptFFF: m_print = m_fff_print; break; case ptSLA: m_print = m_sla_print; break; } + changed = true; } assert(m_print != nullptr); + return changed; } PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 064fcd4b0..981bf6bbf 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -55,7 +55,8 @@ public: void set_finished_event(int event_id) { m_event_finished_id = event_id; } // Activate either m_fff_print or m_sla_print. - void select_technology(PrinterTechnology tech); + // Return true if changed. + bool select_technology(PrinterTechnology tech); // Get the currently active printer technology. PrinterTechnology current_printer_technology() const; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 53d06aee7..5cf872589 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -88,16 +88,18 @@ bool GUI_App::OnInit() // just checking for existence of Slic3r::data_dir is not enough : it may be an empty directory // supplied as argument to --datadir; in that case we should still run the wizard - // eval{ - preset_bundle->setup_directories(); - // }; - // if ($@) { - // warn $@ . "\n"; - // fatal_error(undef, $@); - // } + try { + preset_bundle->setup_directories(); + } catch (const std::exception &ex) { + show_error(nullptr, ex.what()); + // Exit the application. + return false; + } + app_conf_exists = app_config->exists(); // load settings - if (app_conf_exists) app_config->load(); + if (app_conf_exists) + app_config->load(); app_config->set("version", SLIC3R_VERSION); app_config->save(); @@ -109,9 +111,8 @@ bool GUI_App::OnInit() preset_bundle->set_default_suppressed(app_config->get("no_defaults") == "1"); try { preset_bundle->load_presets(*app_config); - } catch (const std::exception & /* ex */) { - // warn $@ . "\n"; - // show_error(undef, $@); + } catch (const std::exception &ex) { + show_error(nullptr, ex.what()); } // Let the libslic3r know the callback, which will translate messages on demand. @@ -168,14 +169,13 @@ bool GUI_App::OnInit() // are shown before or in the same event callback with the main frame creation. // Therefore we schedule them for later using CallAfter. CallAfter([this]() { - // eval{ - if (!preset_updater->config_update()) + try { + if (!preset_updater->config_update()) + mainframe->Close(); + } catch (const std::exception &ex) { + show_error(nullptr, ex.what()); mainframe->Close(); - // }; - // if ($@) { - // show_error(undef, $@); - // mainframe->Close(); - // } + } }); CallAfter([this]() { @@ -675,6 +675,7 @@ void GUI_App::delete_tab_from_list(Tab* tab) void GUI_App::load_current_presets() { PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); + this->plater()->set_printer_technology(printer_technology); for (Tab *tab : tabs_list) if (tab->supports_printer_technology(printer_technology)) { if (tab->name() == "printer") diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f260f72c9..55b2dca40 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -681,10 +681,9 @@ void MainFrame::load_config_file(wxString file/* = wxEmptyString*/) } try { wxGetApp().preset_bundle->load_config_file(file.ToStdString()); - } catch (std::exception & /* ex */) { - // Dont proceed further if the config file cannot be loaded. - // if (Slic3r::GUI::catch_error(this)) - // return; + } catch (const std::exception &ex) { + show_error(this, ex.what()); + return; } wxGetApp().load_current_presets(); wxGetApp().app_config->update_config_dir(get_dir_name(file)); @@ -696,9 +695,9 @@ void MainFrame::export_configbundle() if (!wxGetApp().check_unsaved_changes()) return; // validate current configuration in case it's dirty - auto valid = wxGetApp().preset_bundle->full_config().validate(); - if (!valid.empty()) { -// Slic3r::GUI::catch_error(this); + auto err = wxGetApp().preset_bundle->full_config().validate(); + if (! err.empty()) { + show_error(this, err); return; } // Ask user for a file name. @@ -715,8 +714,8 @@ void MainFrame::export_configbundle() wxGetApp().app_config->update_config_dir(get_dir_name(file)); try { wxGetApp().preset_bundle->export_configbundle(file.ToStdString()); - } catch (std::exception & /* ex */) { -// Slic3r::GUI::catch_error(this); + } catch (const std::exception &ex) { + show_error(this, ex.what()); } } } @@ -742,9 +741,10 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re auto presets_imported = 0; try { - presets_imported = wxGetApp().preset_bundle->load_configbundle(file.ToStdString()); - } catch (std::exception & /* ex */) { -// Slic3r::GUI::catch_error(this) and return; + presets_imported = wxGetApp().preset_bundle->load_configbundle(file.ToStdString()); + } catch (const std::exception &ex) { + show_error(this, ex.what()); + return; } // Load the currently selected preset into the GUI, update the preset selection box. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index af5c79bd3..3941a99a2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -755,7 +755,7 @@ void Sidebar::show_sliced_info_sizer(const bool show) p->sliced_info->Show(show); if (show) { - const PrintStatistics& ps = p->plater->print().print_statistics(); + const PrintStatistics& ps = p->plater->fff_print().print_statistics(); const bool is_wipe_tower = ps.total_wipe_tower_filament > 0; wxString new_label = _(L("Used Filament (m)")); @@ -801,7 +801,7 @@ void Sidebar::show_sliced_info_sizer(const bool show) } // if there is a wipe tower, insert number of toolchanges info into the array: - p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->print().wipe_tower_data().number_of_toolchanges) : "N/A"); + p->sliced_info->SetTextAndShow(siWTNumbetOfToolchanges, is_wipe_tower ? wxString::Format("%.d", p->plater->fff_print().wipe_tower_data().number_of_toolchanges) : "N/A"); } Layout(); @@ -906,7 +906,7 @@ struct Plater::priv // Data Slic3r::DynamicPrintConfig *config; - Slic3r::Print print; + Slic3r::Print fff_print; Slic3r::SLAPrint sla_print; Slic3r::Model model; PrinterTechnology printer_technology = ptFFF; @@ -973,6 +973,8 @@ struct Plater::priv void sla_optimize_rotation(); void split_object(); void split_volume(); + bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } + void update_print_volume_state(); void schedule_background_process(); // Update background processing thread from the current config and Model. enum UpdateBackgroundProcessReturnState { @@ -1061,7 +1063,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif // ENABLE_NEW_MENU_LAYOUT { arranging.store(false); - background_process.set_fff_print(&print); + background_process.set_fff_print(&fff_print); background_process.set_sla_print(&sla_print); background_process.set_gcode_preview_data(&gcode_preview_data); background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED); @@ -1073,7 +1075,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) auto statuscb = [this](const Slic3r::PrintBase::SlicingStatus &status) { wxQueueEvent(this->q, new Slic3r::SlicingStatusEvent(EVT_SLICING_UPDATE, 0, status)); }; - print.set_status_callback(statuscb); + fff_print.set_status_callback(statuscb); sla_print.set_status_callback(statuscb); this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this); @@ -1323,8 +1325,7 @@ std::vector Plater::priv::load_files(const std::vector &input_ if (obj->name.empty()) obj->name = fs::path(obj->input_file).filename().string(); } - } - catch (const std::runtime_error &e) { + } catch (const std::exception &e) { GUI::show_error(q, e.what()); continue; } @@ -1480,13 +1481,15 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType case FT_GCODE: wildcard = file_wildcards[file_type]; break; -// FT_GCODE default: wildcard = file_wildcards[FT_MODEL]; break; } - fs::path output_file(print.output_filepath(std::string())); + // Update printbility state of each of the ModelInstances. + this->update_print_volume_state(); + // Find the file name of the first printable object. + fs::path output_file = this->model.propose_export_file_name(); switch (file_type) { case FT_STL: output_file.replace_extension("stl"); break; @@ -1495,8 +1498,6 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType default: break; } - wxGetApp().preset_bundle->export_selections(print.placeholder_parser()); - auto dlg = Slic3r::make_unique(q, ((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "", true, @@ -1809,6 +1810,15 @@ void Plater::priv::schedule_background_process() this->background_process_timer.Start(500, wxTIMER_ONE_SHOT); } +void Plater::priv::update_print_volume_state() +{ + BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt("bed_shape")->values)); + BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height")))); + // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. + print_volume.min(2) = -1e10; + this->q->model().update_print_volume_state(print_volume); +} + // Update background processing thread from the current config and Model. // Returns a bitmask of UpdateBackgroundProcessReturnState. unsigned int Plater::priv::update_background_process() @@ -1819,16 +1829,10 @@ unsigned int Plater::priv::update_background_process() // If the async_apply_config() was not called by the timer, kill the timer, so the async_apply_config() // will not be called again in vain. this->background_process_timer.Stop(); - - DynamicPrintConfig config = wxGetApp().preset_bundle->full_config(); - BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.opt("bed_shape")->values)); - BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(config.opt_float("max_print_height")))); - // Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced. - print_volume.min(2) = -1e10; - this->q->model().update_print_volume_state(print_volume); - + // Update the "out of print bed" state of ModelInstances. + this->update_print_volume_state(); // Apply new config to the possibly running background task. - Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), std::move(config)); + Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. if (this->canvas3D->is_layers_editing_enabled()) @@ -1880,7 +1884,7 @@ void Plater::priv::async_apply_config() unsigned int state = this->update_background_process(); if (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) this->canvas3D->reload_scene(false); - if ((state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 && this->get_config("background_processing") == "1") { + if ((state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 && this->background_processing_enabled()) { // The print is valid and it can be started. if (this->background_process.start()) this->statusbar()->set_cancel_callback([this]() { @@ -1918,7 +1922,7 @@ void Plater::priv::fix_through_netfabb(const int obj_idx) const auto model_object = model.objects[obj_idx]; Model model_fixed;// = new Model(); - fix_model_by_win10_sdk_gui(*model_object, print, model_fixed); + fix_model_by_win10_sdk_gui(*model_object, this->fff_print, model_fixed); auto new_obj_idxs = load_model_objects(model_fixed.objects); if (new_obj_idxs.empty()) @@ -2235,7 +2239,8 @@ bool Plater::priv::init_object_menu() item_sla_autorot = append_menu_item(&object_menu, wxID_ANY, _(L("Optimize orientation")), _(L("Optimize the rotation of the object for better print results.")), [this](wxCommandEvent&) { sla_optimize_rotation(); }); - if(printer_technology == ptFFF) item_sla_autorot = object_menu.Remove(item_sla_autorot); + if (printer_technology == ptFFF) + item_sla_autorot = object_menu.Remove(item_sla_autorot); // ui updates needs to be binded to the parent panel if (q != nullptr) @@ -2318,9 +2323,12 @@ Plater::~Plater() p->canvas3D = nullptr; } -Sidebar& Plater::sidebar() { return *p->sidebar; } -Model& Plater::model() { return p->model; } -Print& Plater::print() { return p->print; } +Sidebar& Plater::sidebar() { return *p->sidebar; } +Model& Plater::model() { return p->model; } +const Print& Plater::fff_print() const { return p->fff_print; } +Print& Plater::fff_print() { return p->fff_print; } +const SLAPrint& Plater::sla_print() const { return p->sla_print; } +SLAPrint& Plater::sla_print() { return p->sla_print; } #if ENABLE_NEW_MENU_LAYOUT void Plater::load_project() @@ -2509,53 +2517,33 @@ void Plater::export_gcode(fs::path output_path) return; } - std::string err = wxGetApp().preset_bundle->full_config().validate(); - if (err.empty()) - err = p->background_process.validate(); - if (! err.empty()) { - // The config is not valid - GUI::show_error(this, _(err)); + // bitmask of UpdateBackgroundProcessReturnState + unsigned int state = this->p->update_background_process(); + if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) + this->p->canvas3D->reload_scene(false); + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; - } - std::string final_path; - if(printer_technology() == ptFFF) { // TODO: custom button for SLA export + // select output file + if (output_path.empty()) { + // XXX: take output path from CLI opts? Ancient Slic3r versions used to do that... - // Copy the names of active presets into the placeholder parser. - wxGetApp().preset_bundle->export_selections(p->print.placeholder_parser()); - - // select output file - if (output_path.empty()) { - // XXX: take output path from CLI opts? Ancient Slic3r versions used to do that... - - // If possible, remove accents from accented latin characters. - // This function is useful for generating file names to be processed by legacy firmwares. - auto default_output_file = fs::path(Slic3r::fold_utf8_to_ascii( - p->print.output_filepath(output_path.string()) - // FIXME: ^ errors to handle? - )); - auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); - - wxFileDialog dlg(this, _(L("Save G-code file as:")), - start_dir, - default_output_file.filename().string(), - GUI::file_wildcards[FT_GCODE], - wxFD_SAVE | wxFD_OVERWRITE_PROMPT - ); - - if (dlg.ShowModal() == wxID_OK) { - fs::path path(dlg.GetPath()); - wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); - output_path = path; - } + // If possible, remove accents from accented latin characters. + // This function is useful for generating file names to be processed by legacy firmwares. + fs::path default_output_file; + try { + default_output_file = this->p->background_process.current_print()->output_filepath(output_path.string()); + } catch (const std::exception &ex) { + show_error(this, ex.what()); + return; } + default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); + auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); - final_path = p->print.output_filepath(output_path.string()); - } else { - wxFileDialog dlg(this, _(L("Save Zip file as:")), - wxGetApp().app_config->get_last_output_dir(""), - "out.zip", - GUI::file_wildcards[FT_PNGZIP], + wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")), + start_dir, + default_output_file.filename().string(), + GUI::file_wildcards[(printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP], wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); @@ -2564,13 +2552,25 @@ void Plater::export_gcode(fs::path output_path) wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); output_path = path; } - - final_path = output_path.string(); + } else { + try { + output_path = this->p->background_process.current_print()->output_filepath(output_path.string()); + } catch (const std::exception &ex) { + show_error(this, ex.what()); + return; + } } - if (! output_path.empty()) { - this->p->background_process.schedule_export(final_path); - this->p->background_process.start(); + if (! output_path.empty()) + this->p->background_process.schedule_export(output_path.string()); + + if ((! output_path.empty() || this->p->background_processing_enabled()) && ! this->p->background_process.running()) { + // The print is valid and it should be started. + if (this->p->background_process.start()) + this->p->statusbar()->set_cancel_callback([this]() { + this->p->statusbar()->set_status_text(L("Cancelling")); + this->p->background_process.stop(); + }); } } @@ -2660,11 +2660,8 @@ void Plater::reslice() unsigned int state = this->p->update_background_process(); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) this->p->canvas3D->reload_scene(false); - if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running()) { + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && ! this->p->background_process.running()) { // The print is valid and it can be started. - // Copy the names of active presets into the placeholder parser. - //FIXME how to generate a file name for the SLA printers? - wxGetApp().preset_bundle->export_selections(this->print().placeholder_parser()); if (this->p->background_process.start()) this->p->statusbar()->set_cancel_callback([this]() { this->p->statusbar()->set_status_text(L("Cancelling")); @@ -2709,12 +2706,8 @@ void Plater::on_config_change(const DynamicPrintConfig &config) bool update_scheduled = false; for (auto opt_key : p->config->diff(config)) { p->config->set_key_value(opt_key, config.option(opt_key)->clone()); - if (opt_key == "printer_technology") { - p->printer_technology = config.opt_enum(opt_key); - p->background_process.select_technology(this->printer_technology()); - //FIXME for SLA synchronize - //p->background_process.apply(Model)! - } + if (opt_key == "printer_technology") + this->set_printer_technology(config.opt_enum(opt_key)); else if (opt_key == "bed_shape") { this->p->canvas3D->set_bed_shape(p->config->option(opt_key)->values); if (p->preview) p->preview->set_bed_shape(p->config->option(opt_key)->values); @@ -2811,6 +2804,16 @@ PrinterTechnology Plater::printer_technology() const return p->printer_technology; } +void Plater::set_printer_technology(PrinterTechnology printer_technology) +{ + p->printer_technology = printer_technology; + if (p->background_process.select_technology(printer_technology)) { + // Update the active presets. + } + //FIXME for SLA synchronize + //p->background_process.apply(Model)! +} + void Plater::changed_object(int obj_idx) { if (obj_idx < 0) @@ -2827,20 +2830,21 @@ void Plater::changed_object(int obj_idx) model_object->center_around_origin(); #endif // !ENABLE_MODELVOLUME_TRANSFORM model_object->ensure_on_bed(); + if (this->p->printer_technology == ptSLA) { + // Update the SLAPrint from the current Model, so that the reload_scene() + // pulls the correct data. + this->p->update_background_process(); + } p->canvas3D->reload_scene(false); } // update print + this->p->schedule_background_process(); if (list->is_parts_changed() || list->is_part_settings_changed()) { - this->p->schedule_background_process(); #if !ENABLE_MODIFIED_CAMERA_TARGET p->canvas3D->zoom_to_volumes(); #endif // !ENABLE_MODIFIED_CAMERA_TARGET } - else { - this->p->schedule_background_process(); - } - } void Plater::fix_through_netfabb(const int obj_idx) { p->fix_through_netfabb(obj_idx); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 4a6140932..feb3bcb0f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -111,7 +111,10 @@ public: Sidebar& sidebar(); Model& model(); - Print& print(); + const Print& fff_print() const; + Print& fff_print(); + const SLAPrint& sla_print() const; + SLAPrint& sla_print(); #if ENABLE_NEW_MENU_LAYOUT void load_project(); @@ -168,7 +171,9 @@ public: bool is_single_full_object_selection() const; GLCanvas3D* canvas3D(); - PrinterTechnology printer_technology() const; + PrinterTechnology printer_technology() const; + void set_printer_technology(PrinterTechnology printer_technology); + private: struct priv; std::unique_ptr p; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index f26ec24a4..38d8ef96c 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -363,39 +363,6 @@ const std::vector& Preset::nozzle_options() return s_opts; } -const std::vector& Preset::sla_printer_options() -{ - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "printer_technology", - "bed_shape", "max_print_height", - "display_width", "display_height", "display_pixels_x", "display_pixels_y", - "printer_correction", - "printer_notes", - "inherits" - }; - } - return s_opts; -} - -const std::vector& Preset::sla_material_options() -{ - static std::vector s_opts; - if (s_opts.empty()) { - s_opts = { - "layer_height", "initial_layer_height", - "exposure_time", "initial_exposure_time", - "material_correction_printing", "material_correction_curing", - "material_notes", - "default_sla_material_profile", - "compatible_printers", - "compatible_printers_condition", "inherits" - }; - } - return s_opts; -} - const std::vector& Preset::sla_print_options() { static std::vector s_opts; @@ -418,6 +385,7 @@ const std::vector& Preset::sla_print_options() "pad_wall_height", "pad_max_merge_distance", "pad_edge_radius", + "output_filename_format", "default_sla_print_profile", "compatible_printers", "compatible_printers_condition", @@ -427,6 +395,39 @@ const std::vector& Preset::sla_print_options() return s_opts; } +const std::vector& Preset::sla_material_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "initial_layer_height", + "exposure_time", "initial_exposure_time", + "material_correction_printing", "material_correction_curing", + "material_notes", + "default_sla_material_profile", + "compatible_printers", + "compatible_printers_condition", "inherits" + }; + } + return s_opts; +} + +const std::vector& Preset::sla_printer_options() +{ + static std::vector s_opts; + if (s_opts.empty()) { + s_opts = { + "printer_technology", + "bed_shape", "max_print_height", + "display_width", "display_height", "display_pixels_x", "display_pixels_y", + "printer_correction", + "printer_notes", + "inherits" + }; + } + return s_opts; +} + PresetCollection::PresetCollection(Preset::Type type, const std::vector &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &default_name) : m_type(type), m_edited_preset(type, "", false), @@ -539,8 +540,8 @@ static bool profile_print_params_same(const DynamicPrintConfig &cfg1, const Dyna // Following keys are used by the UI, not by the slicing core, therefore they are not important // when comparing profiles for equality. Ignore them. for (const char *key : { "compatible_printers", "compatible_printers_condition", "inherits", - "print_settings_id", "filament_settings_id", "sla_material_settings_id", "printer_settings_id", - "printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_material_profile" }) + "print_settings_id", "filament_settings_id", "sla_print_settings_id", "sla_material_settings_id", "printer_settings_id", + "printer_model", "printer_variant", "default_print_profile", "default_filament_profile", "default_sla_print_profile", "default_sla_material_profile" }) diff.erase(std::remove(diff.begin(), diff.end(), key), diff.end()); // Preset with the same name as stored inside the config exists. return diff.empty(); @@ -562,7 +563,7 @@ Preset& PresetCollection::load_external_preset( bool select) { // Load the preset over a default preset, so that the missing fields are filled in from the default preset. - DynamicPrintConfig cfg(this->default_preset().config); + DynamicPrintConfig cfg(this->default_preset_for(config).config); cfg.apply_only(config, cfg.keys(), true); // Is there a preset already loaded with the name stored inside the config? std::deque::iterator it = this->find_preset_internal(original_name); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 83c4364a2..f2a0b69a0 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -26,7 +26,6 @@ #include #include "libslic3r/libslic3r.h" -#include "libslic3r/PlaceholderParser.hpp" #include "libslic3r/Utils.hpp" // Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir. @@ -95,8 +94,8 @@ PresetBundle::PresetBundle() : preset.config.option("default_filament_profile", true)->values = { "" }; } else { - preset.config.optptr("default_sla_material_profile", true); preset.config.optptr("default_sla_print_profile", true); + preset.config.optptr("default_sla_material_profile", true); } // default_sla_material_profile preset.inherits(); @@ -391,24 +390,7 @@ void PresetBundle::export_selections(AppConfig &config) } config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); - config.set("presets", "printer", printers.get_selected_preset_name()); -} - -void PresetBundle::export_selections(PlaceholderParser &pp) -{ - assert(filament_presets.size() >= 1); - assert(filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front()); - switch (printers.get_edited_preset().printer_technology()) { - case ptFFF: - pp.set("print_preset", prints.get_selected_preset().name); - pp.set("filament_preset", filament_presets); - break; - case ptSLA: - pp.set("sla_print_preset", sla_prints.get_selected_preset().name); - pp.set("sla_material_preset", sla_materials.get_selected_preset().name); - break; - } - pp.set("printer_preset", printers.get_selected_preset().name); + config.set("presets", "printer", printers.get_selected_preset_name()); } bool PresetBundle::load_compatible_bitmaps() @@ -676,7 +658,8 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool size_t num_extruders = (printer_technology == ptFFF) ? std::min(config.option("nozzle_diameter" )->values.size(), config.option("filament_diameter")->values.size()) : - 0; + // 1 SLA material + 1; // Make a copy of the "compatible_printers_condition_cummulative" and "inherits_cummulative" vectors, which // accumulate values over all presets (print, filaments, printers). // These values will be distributed into their particular presets when loading. @@ -687,8 +670,16 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool compatible_printers_condition_values.resize(num_extruders + 2, std::string()); inherits_values.resize(num_extruders + 2, std::string()); // The "default_filament_profile" will be later extracted into the printer profile. - if (printer_technology == ptFFF) - config.option("default_filament_profile", true)->values.resize(num_extruders, std::string()); + switch (printer_technology) { + case ptFFF: + config.option("default_print_profile", true); + config.option("default_filament_profile", true)->values.resize(num_extruders, std::string()); + break; + case ptSLA: + config.option("default_sla_print_profile", true); + config.option("default_sla_material_profile", true); + break; + } // 1) Create a name from the file name. // Keep the suffix (.ini, .gcode, .amf, .3mf etc) to differentiate it from the normal profiles. @@ -697,22 +688,24 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // 2) If the loading succeeded, split and load the config into print / filament / printer settings. // First load the print and printer presets. - // #ys_FIXME_SLA_PRINT - for (size_t i_group = 0; i_group < 2; ++ i_group) { - PresetCollection &presets = (i_group == 0) ? ((printer_technology == ptFFF) ? this->prints : this->sla_materials) : this->printers; - // Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles. - size_t idx = (i_group == 0) ? 0 : num_extruders + 1; - inherits = inherits_values[idx]; - compatible_printers_condition = compatible_printers_condition_values[idx]; + auto load_preset = + [&config, &inherits, &inherits_values, &compatible_printers_condition, &compatible_printers_condition_values, is_external, &name, &name_or_path] + (PresetCollection &presets, size_t idx, const std::string &key) { + // Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles. + inherits = inherits_values[idx]; + compatible_printers_condition = compatible_printers_condition_values[idx]; if (is_external) - presets.load_external_preset(name_or_path, name, - config.opt_string((i_group == 0) ? ((printer_technology == ptFFF) ? "print_settings_id" : "sla_material_settings_id") : "printer_settings_id", true), - config); - else - presets.load_preset(presets.path_from_name(name), name, config).save(); - } + presets.load_external_preset(name_or_path, name, config.opt_string(key, true), config); + else + presets.load_preset(presets.path_from_name(name), name, config).save(); + }; + + switch (Preset::printer_technology(config)) { + case ptFFF: + { + load_preset(this->prints, 0, "print_settings_id"); + load_preset(this->printers, num_extruders + 1, "printer_settings_id"); - if (Preset::printer_technology(config) == ptFFF) { // 3) Now load the filaments. If there are multiple filament presets, split them and load them. auto old_filament_profile_names = config.option("filament_settings_id", true); old_filament_profile_names->values.resize(num_extruders, std::string()); @@ -721,12 +714,15 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool // Split the "compatible_printers_condition" and "inherits" from the cummulative vectors to separate filament presets. inherits = inherits_values[1]; compatible_printers_condition = compatible_printers_condition_values[1]; - if (is_external) - this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config); - else - this->filaments.load_preset(this->filaments.path_from_name(name), name, config).save(); + Preset *loaded = nullptr; + if (is_external) { + loaded = &this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config); + } else { + loaded = &this->filaments.load_preset(this->filaments.path_from_name(name), name, config); + loaded->save(); + } this->filament_presets.clear(); - this->filament_presets.emplace_back(name); + this->filament_presets.emplace_back(loaded->name); } else { // Split the filament presets, load each of them separately. std::vector configs(num_extruders, this->filaments.default_preset().config); @@ -772,12 +768,18 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool this->filament_presets.emplace_back(loaded->name); } } - // 4) Load the project config values (the per extruder wipe matrix etc). this->project_config.apply_only(config, s_project_options); + break; + } + case ptSLA: + load_preset(this->sla_prints, 0, "sla_print_settings_id"); + load_preset(this->sla_materials, 1, "sla_material_settings_id"); + load_preset(this->printers, 2, "printer_settings_id"); + break; } - this->update_compatible_with_printer(false); + this->update_compatible_with_printer(false); } // Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file. @@ -1229,7 +1231,9 @@ void PresetBundle::update_compatible_with_printer(bool select_other_if_incompati switch (printers.get_edited_preset().printer_technology()) { case ptFFF: { - const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile"); + assert(printer_preset.config.has("default_print_profile")); + assert(printer_preset.config.has("default_filament_profile")); + const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile"); const std::vector &prefered_filament_profiles = printer_preset.config.option("default_filament_profile")->values; prefered_print_profile.empty() ? this->prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : @@ -1263,18 +1267,18 @@ void PresetBundle::update_compatible_with_printer(bool select_other_if_incompati } case ptSLA: { - const std::string &prefered_sla_material_profile = printer_preset.config.opt_string("default_sla_material_profile"); + assert(printer_preset.config.has("default_sla_print_profile")); + assert(printer_preset.config.has("default_sla_material_profile")); + const std::string &prefered_sla_print_profile = printer_preset.config.opt_string("default_sla_print_profile"); + (prefered_sla_print_profile.empty()) ? + this->sla_prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : + this->sla_prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible, + [&prefered_sla_print_profile](const std::string& profile_name){ return profile_name == prefered_sla_print_profile; }); + const std::string &prefered_sla_material_profile = printer_preset.config.opt_string("default_sla_material_profile"); prefered_sla_material_profile.empty() ? this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : this->sla_materials.update_compatible_with_printer(printer_preset, select_other_if_incompatible, [&prefered_sla_material_profile](const std::string& profile_name){ return profile_name == prefered_sla_material_profile; }); - - const std::string &prefered_sla_print_profile = printer_preset.config.opt_string("default_sla_print_profile"); - prefered_sla_print_profile.empty() ? - this->sla_prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible) : - this->sla_prints.update_compatible_with_printer(printer_preset, select_other_if_incompatible, - [&prefered_sla_print_profile](const std::string& profile_name){ return profile_name == prefered_sla_print_profile; }); - break; } } diff --git a/src/slic3r/GUI/PresetBundle.hpp b/src/slic3r/GUI/PresetBundle.hpp index 06ae95094..930a9e9b8 100644 --- a/src/slic3r/GUI/PresetBundle.hpp +++ b/src/slic3r/GUI/PresetBundle.hpp @@ -13,8 +13,6 @@ namespace GUI { class BitmapCache; }; -class PlaceholderParser; - // Bundle of Print + Filament + Printer presets. class PresetBundle { @@ -35,8 +33,6 @@ public: // Export selections (current print, current filaments, current printer) into config.ini void export_selections(AppConfig &config); - // Export selections (current print, current filaments, current printer) into a placeholder parser. - void export_selections(PlaceholderParser &pp); PresetCollection prints; PresetCollection sla_prints; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c71f4dbae..259571585 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3042,6 +3042,12 @@ void TabSLAPrint::build() optgroup->append_single_option_line("pad_max_merge_distance"); optgroup->append_single_option_line("pad_edge_radius"); + page = add_options_page(_(L("Output options")), "page_white_go.png"); + optgroup = page->new_optgroup(_(L("Output file"))); + Option option = optgroup->get_option("output_filename_format"); + option.opt.full_width = true; + optgroup->append_single_option_line(option); + page = add_options_page(_(L("Dependencies")), "wrench.png"); optgroup = page->new_optgroup(_(L("Profile dependencies"))); Line line = optgroup->create_single_option_line("compatible_printers");//Line { _(L("Compatible printers")), "" }; @@ -3050,7 +3056,7 @@ void TabSLAPrint::build() }; optgroup->append_line(line, &m_colored_Label); - Option option = optgroup->get_option("compatible_printers_condition"); + option = optgroup->get_option("compatible_printers_condition"); option.opt.full_width = true; optgroup->append_single_option_line(option); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index f6edb5d64..5d17ac258 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -184,11 +184,4 @@ _constant() } %}; - void export_png(char *path) %code%{ - try { - THIS->export_png(path); - } catch (std::exception& e) { - croak(e.what()); - } - %}; };