From d46d0dc3657937eb89db68d2bee9e07fe32422ce Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 3 Dec 2018 13:14:28 +0100 Subject: [PATCH] Implemented naming of the SLA export file based on the output file name template. Reworked naming of the plater exports to not use the output file name template, but to derive the file name from the first printable object's name. Fixed error handling: Reimpemented the Perl's "eval" blocks as try / catch blocks. --- src/libslic3r/Model.cpp | 9 + src/libslic3r/Model.hpp | 3 + src/libslic3r/PlaceholderParser.cpp | 65 +++++-- src/libslic3r/PlaceholderParser.hpp | 6 + src/libslic3r/Print.cpp | 94 ++-------- src/libslic3r/Print.hpp | 13 +- src/libslic3r/PrintBase.cpp | 81 +++++++++ src/libslic3r/PrintBase.hpp | 26 ++- src/libslic3r/PrintConfig.cpp | 3 +- src/libslic3r/PrintConfig.hpp | 21 ++- src/libslic3r/SLAPrint.cpp | 29 +++- src/libslic3r/SLAPrint.hpp | 4 + src/slic3r.cpp | 2 +- src/slic3r/GUI/3DScene.cpp | 2 + src/slic3r/GUI/BackgroundSlicingProcess.cpp | 5 +- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 3 +- src/slic3r/GUI/GUI_App.cpp | 37 ++-- src/slic3r/GUI/MainFrame.cpp | 24 +-- src/slic3r/GUI/Plater.cpp | 180 ++++++++++---------- src/slic3r/GUI/Plater.hpp | 9 +- src/slic3r/GUI/Preset.cpp | 73 ++++---- src/slic3r/GUI/PresetBundle.cpp | 110 ++++++------ src/slic3r/GUI/PresetBundle.hpp | 4 - src/slic3r/GUI/Tab.cpp | 8 +- xs/xsp/Print.xsp | 7 - 25 files changed, 474 insertions(+), 344 deletions(-) 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()); - } - %}; };