diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 8e48a56d6..9ef2b8896 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -729,7 +729,7 @@ void GCode::_do_export(Print &print, FILE *file) // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); - print.update_object_placeholders(m_placeholder_parser.config_writable()); + print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode"); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. // For a print by objects, find the 1st printing object. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 93d2fefe2..29bbb49fe 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1875,12 +1875,12 @@ int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion // Generate a recommended G-code output file name based on the format template, default extension, and template parameters // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized). -std::string Print::output_filename() const +std::string Print::output_filename(const std::string &filename_base) const { // Set the placeholders for the data know first after the G-code export is finished. // These values will be just propagated into the output file name. DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); - return this->PrintBase::output_filename(m_config.output_filename_format.value, "gcode", &config); + return this->PrintBase::output_filename(m_config.output_filename_format.value, ".gcode", filename_base, &config); } /* // Shorten the dhms time by removing the seconds, rounding the dhm to full minutes diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index b566eaded..be2a9a3bd 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -351,7 +351,7 @@ public: bool has_wipe_tower() const; const WipeTowerData& wipe_tower_data() const { return m_wipe_tower_data; } - std::string output_filename() const override; + std::string output_filename(const std::string &filename_base = std::string()) const override; // Accessed by SupportMaterial const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 412aae338..fdee67f2e 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -15,7 +15,7 @@ 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(DynamicConfig &config) const +void PrintBase::update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const { // get the first input file name std::string input_file; @@ -40,25 +40,29 @@ void PrintBase::update_object_placeholders(DynamicConfig &config) const config.set_key_value("year", new ConfigOptionStrings(v_scale)); if (! input_file.empty()) { // get basename with and without suffix - const std::string input_basename = boost::filesystem::path(input_file).filename().string(); - config.set_key_value("input_filename", new ConfigOptionString(input_basename)); - const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); - config.set_key_value("input_filename_base", new ConfigOptionString(input_basename_base)); + const std::string input_filename = boost::filesystem::path(input_file).filename().string(); + const std::string input_filename_base = input_filename.substr(0, input_filename.find_last_of(".")); + config.set_key_value("input_filename", new ConfigOptionString(input_filename_base + default_ext)); + config.set_key_value("input_filename_base", new ConfigOptionString(input_filename_base)); } } // Generate an output file name based on the format template, default extension, and template parameters // (timestamps, object placeholders derived from the model, current placeholder prameters, print statistics - config_override) -std::string PrintBase::output_filename(const std::string &format, const std::string &default_ext, const DynamicConfig *config_override) const +std::string PrintBase::output_filename(const std::string &format, const std::string &default_ext, const std::string &filename_base, const DynamicConfig *config_override) const { DynamicConfig cfg; if (config_override != nullptr) cfg = *config_override; PlaceholderParser::update_timestamp(cfg); - this->update_object_placeholders(cfg); + this->update_object_placeholders(cfg, default_ext); + if (! filename_base.empty()) { + cfg.set_key_value("input_filename", new ConfigOptionString(filename_base + default_ext)); + cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base)); + } try { boost::filesystem::path filename = format.empty() ? - cfg.opt_string("input_filename_base") + "." + default_ext : + cfg.opt_string("input_filename_base") + default_ext : this->placeholder_parser().process(format, 0, &cfg); if (filename.extension().empty()) filename = boost::filesystem::change_extension(filename, default_ext); @@ -68,17 +72,17 @@ std::string PrintBase::output_filename(const std::string &format, const std::str } } -std::string PrintBase::output_filepath(const std::string &path) const +std::string PrintBase::output_filepath(const std::string &path, const std::string &filename_base) 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 - return (boost::filesystem::path(m_model.propose_export_file_name_and_path()).parent_path() / this->output_filename()).make_preferred().string(); + return (boost::filesystem::path(m_model.propose_export_file_name_and_path()).parent_path() / this->output_filename(filename_base)).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(); + return (p / this->output_filename(filename_base)).make_preferred().string(); // if we were supplied a file which is not a directory, use it return path; diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 2d47d3567..a4ef67117 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -318,8 +318,10 @@ public: 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; + virtual std::string output_filename(const std::string &filename_base = std::string()) const = 0; + // If the filename_base is set, it is used as the input for the template processing. In that case the path is expected to be the directory (may be empty). + // If filename_set is empty, than the path may be a file or directory. If it is a file, then the macro will not be processed. + std::string output_filepath(const std::string &path, const std::string &filename_base = std::string()) const; protected: friend class PrintObjectBase; @@ -334,9 +336,9 @@ protected: 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 DynamicConfig *config_override = nullptr) const; + std::string output_filename(const std::string &format, const std::string &default_ext, const std::string &filename_base, const DynamicConfig *config_override = nullptr) const; // Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects. - void update_object_placeholders(DynamicConfig &config) const; + void update_object_placeholders(DynamicConfig &config, const std::string &default_ext) const; Model m_model; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 6883bb2f2..457be23ba 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -561,10 +561,10 @@ void SLAPrint::finalize() // Generate a recommended output file name based on the format template, default extension, and template parameters // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before the output is finalized). -std::string SLAPrint::output_filename() const +std::string SLAPrint::output_filename(const std::string &filename_base) const { DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); - return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "sl1", &config); + return this->PrintBase::output_filename(m_print_config.output_filename_format.value, ".sl1", filename_base, &config); } namespace { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 64b2160b0..dea468e7a 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -402,9 +402,9 @@ public: // Extracted value from the configuration objects Vec3d relative_correction() const; - std::string output_filename() const override; + std::string output_filename(const std::string &filename_base = std::string()) const override; - const SLAPrintStatistics& print_statistics() const { return m_print_statistics; } + const SLAPrintStatistics& print_statistics() const { return m_print_statistics; } std::string validate() const override; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index d895a61d4..94fb6481b 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -67,6 +67,14 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const return m_print->technology(); } +std::string BackgroundSlicingProcess::output_filepath_for_project(const boost::filesystem::path &project_path) +{ + assert(m_print != nullptr); + if (project_path.empty()) + return m_print->output_filepath(""); + return m_print->output_filepath(project_path.parent_path().string(), project_path.stem().string()); +} + // This function may one day be merged into the Print, but historically the print was separated // from the G-code generator. void BackgroundSlicingProcess::process_fff() diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 9ea20163d..e0a1960da 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include "libslic3r/Print.hpp" @@ -65,6 +67,9 @@ public: const PrintBase* current_print() const { return m_print; } const Print* fff_print() const { return m_fff_print; } const SLAPrint* sla_print() const { return m_sla_print; } + // Take the project path (if provided), extract the name of the project, run it through the macro processor and save it next to the project file. + // If the project_path is empty, just run output_filepath(). + std::string output_filepath_for_project(const boost::filesystem::path &project_path); // Start the background processing. Returns false if the background processing was already running. bool start(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c63ae73f2..eece32b9b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5,9 +5,7 @@ #include #include #include -#include -#include -#include +#include #include #include @@ -2971,9 +2969,16 @@ wxString Plater::priv::get_project_filename(const wxString& extension) const void Plater::priv::set_project_filename(const wxString& filename) { boost::filesystem::path full_path = into_path(filename); - // remove extension - while (full_path.has_extension()) - { + boost::filesystem::path ext = full_path.extension(); + if (boost::iequals(ext.string(), ".amf")) { + // Remove the first extension. + full_path.replace_extension(""); + // It may be ".zip.amf". + if (boost::iequals(full_path.extension().string(), ".zip")) + // Remove the 2nd extension. + full_path.replace_extension(""); + } else { + // Remove just one extension. full_path.replace_extension(""); } @@ -3492,7 +3497,7 @@ void Plater::export_gcode() unsigned int state = this->p->update_restart_background_process(false, false); if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) return; - default_output_file = this->p->background_process.current_print()->output_filepath(into_path(get_project_filename()).string()); + default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf"))); } catch (const std::exception &ex) { show_error(this, ex.what()); @@ -3737,7 +3742,7 @@ void Plater::send_gcode() unsigned int state = this->p->update_restart_background_process(false, false); if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) return; - default_output_file = this->p->background_process.current_print()->output_filepath(into_path(get_project_filename(".3mf")).string()); + default_output_file = this->p->background_process.output_filepath_for_project(into_path(get_project_filename(".3mf"))); } catch (const std::exception &ex) { show_error(this, ex.what());