mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-13 21:25:53 +08:00
Merge remote-tracking branch 'remotes/prusa/stable' into dev
need to merge profiles need to test & iron out the PresetConfigSubstitutions thingy
This commit is contained in:
commit
f87b5fccde
71
resources/icons/info.svg
Normal file
71
resources/icons/info.svg
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
id="error"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 200 200"
|
||||||
|
enable-background="new 0 0 100 100"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="notification_error.svg"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||||
|
id="metadata19"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs17" /><sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1377"
|
||||||
|
id="namedview15"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="5.04"
|
||||||
|
inkscape:cx="117.17146"
|
||||||
|
inkscape:cy="98.609664"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="error" />
|
||||||
|
<g
|
||||||
|
id="g4"
|
||||||
|
transform="matrix(2.52,0,0,2.52,-26,-26)">
|
||||||
|
<path
|
||||||
|
fill="#808080"
|
||||||
|
d="m 50,54.25 c -2.35,0 -4.25,-1.9 -4.25,-4.25 V 35 c 0,-2.35 1.9,-4.25 4.25,-4.25 2.35,0 4.25,1.9 4.25,4.25 v 15 c 0,2.35 -1.9,4.25 -4.25,4.25 z"
|
||||||
|
id="path2" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g8"
|
||||||
|
transform="matrix(2.52,0,0,2.52,-26,-26)">
|
||||||
|
<circle
|
||||||
|
fill="#808080"
|
||||||
|
cx="50"
|
||||||
|
cy="65"
|
||||||
|
r="5"
|
||||||
|
id="circle6" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g12"
|
||||||
|
transform="matrix(2.52,0,0,2.52,-26,-26)">
|
||||||
|
<path
|
||||||
|
fill="#808080"
|
||||||
|
d="M 50,89.25 C 28.36,89.25 10.75,71.64 10.75,50 10.75,28.36 28.36,10.75 50,10.75 71.64,10.75 89.25,28.36 89.25,50 89.25,71.64 71.64,89.25 50,89.25 Z m 0,-70 C 33.05,19.25 19.25,33.04 19.25,50 19.25,66.95 33.04,80.75 50,80.75 66.95,80.75 80.75,66.96 80.75,50 80.75,33.05 66.95,19.25 50,19.25 Z"
|
||||||
|
id="path10" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
71
resources/icons/white/info.svg
Normal file
71
resources/icons/white/info.svg
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.0"
|
||||||
|
id="error"
|
||||||
|
x="0px"
|
||||||
|
y="0px"
|
||||||
|
viewBox="0 0 200 200"
|
||||||
|
enable-background="new 0 0 100 100"
|
||||||
|
xml:space="preserve"
|
||||||
|
sodipodi:docname="notification_error.svg"
|
||||||
|
width="200"
|
||||||
|
height="200"
|
||||||
|
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||||
|
id="metadata19"><rdf:RDF><cc:Work
|
||||||
|
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
|
id="defs17" /><sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1377"
|
||||||
|
id="namedview15"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="5.04"
|
||||||
|
inkscape:cx="117.17146"
|
||||||
|
inkscape:cy="98.609664"
|
||||||
|
inkscape:window-x="-8"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="error" />
|
||||||
|
<g
|
||||||
|
id="g4"
|
||||||
|
transform="matrix(2.52,0,0,2.52,-26,-26)">
|
||||||
|
<path
|
||||||
|
fill="#FFFFFF"
|
||||||
|
d="m 50,54.25 c -2.35,0 -4.25,-1.9 -4.25,-4.25 V 35 c 0,-2.35 1.9,-4.25 4.25,-4.25 2.35,0 4.25,1.9 4.25,4.25 v 15 c 0,2.35 -1.9,4.25 -4.25,4.25 z"
|
||||||
|
id="path2" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g8"
|
||||||
|
transform="matrix(2.52,0,0,2.52,-26,-26)">
|
||||||
|
<circle
|
||||||
|
fill="#FFFFFF"
|
||||||
|
cx="50"
|
||||||
|
cy="65"
|
||||||
|
r="5"
|
||||||
|
id="circle6" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g12"
|
||||||
|
transform="matrix(2.52,0,0,2.52,-26,-26)">
|
||||||
|
<path
|
||||||
|
fill="#FFFFFF"
|
||||||
|
d="M 50,89.25 C 28.36,89.25 10.75,71.64 10.75,50 10.75,28.36 28.36,10.75 50,10.75 71.64,10.75 89.25,28.36 89.25,50 89.25,71.64 71.64,89.25 50,89.25 Z m 0,-70 C 33.05,19.25 19.25,33.04 19.25,50 19.25,66.95 33.04,80.75 50,80.75 66.95,80.75 80.75,66.96 80.75,50 80.75,33.05 66.95,19.25 50,19.25 Z"
|
||||||
|
id="path10" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
@ -110,7 +110,8 @@ int CLI::run(int argc, char **argv)
|
|||||||
boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), GCODEVIEWER_APP_CMD);
|
boost::algorithm::iends_with(boost::filesystem::path(argv[0]).filename().string(), GCODEVIEWER_APP_CMD);
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
|
const std::vector<std::string> &load_configs = m_config.option<ConfigOptionStrings>("load", true)->values;
|
||||||
|
const ForwardCompatibilitySubstitutionRule config_substitution_rule = m_config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>("config_compatibility", true)->value;
|
||||||
|
|
||||||
// load config files supplied via --load
|
// load config files supplied via --load
|
||||||
for (auto const &file : load_configs) {
|
for (auto const &file : load_configs) {
|
||||||
@ -122,13 +123,19 @@ int CLI::run(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
|
ConfigSubstitutions config_substitutions;
|
||||||
try {
|
try {
|
||||||
config.load(file);
|
config_substitutions = config.load(file, config_substitution_rule);
|
||||||
} catch (std::exception &ex) {
|
} catch (std::exception &ex) {
|
||||||
boost::nowide::cerr << "Error while reading config file: " << ex.what() << std::endl;
|
boost::nowide::cerr << "Error while reading config file \"" << file << "\": " << ex.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (! config_substitutions.empty()) {
|
||||||
|
boost::nowide::cout << "The following configuration values were substituted when loading \" << file << \":\n";
|
||||||
|
for (const ConfigSubstitution &subst : config_substitutions)
|
||||||
|
boost::nowide::cout << "\tkey = \"" << subst.opt_def->opt_key << "\"\t loaded = \"" << subst.old_value << "\tsubstituted = \"" << subst.new_value->serialize() << "\"\n";
|
||||||
|
}
|
||||||
config.normalize_fdm();
|
config.normalize_fdm();
|
||||||
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
||||||
if (printer_technology == ptUnknown) {
|
if (printer_technology == ptUnknown) {
|
||||||
@ -166,7 +173,9 @@ int CLI::run(int argc, char **argv)
|
|||||||
try {
|
try {
|
||||||
// When loading an AMF or 3MF, config is imported as well, including the printer technology.
|
// When loading an AMF or 3MF, config is imported as well, including the printer technology.
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
model = Model::read_from_file(file, &config, true);
|
ConfigSubstitutionContext config_substitutions(config_substitution_rule);
|
||||||
|
//FIXME should we check the version here? // | Model::LoadAttribute::CheckVersion ?
|
||||||
|
model = Model::read_from_file(file, &config, &config_substitutions, Model::LoadAttribute::AddDefaultInstances);
|
||||||
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
PrinterTechnology other_printer_technology = Slic3r::printer_technology(config);
|
||||||
if (printer_technology == ptUnknown) {
|
if (printer_technology == ptUnknown) {
|
||||||
printer_technology = other_printer_technology;
|
printer_technology = other_printer_technology;
|
||||||
@ -175,6 +184,11 @@ int CLI::run(int argc, char **argv)
|
|||||||
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
|
boost::nowide::cerr << "Mixing configurations for FFF and SLA technologies" << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (! config_substitutions.substitutions.empty()) {
|
||||||
|
boost::nowide::cout << "The following configuration values were substituted when loading \" << file << \":\n";
|
||||||
|
for (const ConfigSubstitution& subst : config_substitutions.substitutions)
|
||||||
|
boost::nowide::cout << "\tkey = \"" << subst.opt_def->opt_key << "\"\t loaded = \"" << subst.old_value << "\tsubstituted = \"" << subst.new_value->serialize() << "\"\n";
|
||||||
|
}
|
||||||
// config is applied to m_print_config before the current m_config values.
|
// config is applied to m_print_config before the current m_config values.
|
||||||
config += std::move(m_print_config);
|
config += std::move(m_print_config);
|
||||||
m_print_config = std::move(config);
|
m_print_config = std::move(config);
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "AppConfig.hpp"
|
#include "AppConfig.hpp"
|
||||||
#include "Exception.hpp"
|
#include "Exception.hpp"
|
||||||
#include "Thread.hpp"
|
#include "Thread.hpp"
|
||||||
|
#include "format.hpp"
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -16,9 +17,13 @@
|
|||||||
#include <boost/property_tree/ptree_fwd.hpp>
|
#include <boost/property_tree/ptree_fwd.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/format/format_fwd.hpp>
|
#include <boost/format/format_fwd.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
//#include <wx/string.h>
|
#ifdef WIN32
|
||||||
//#include "I18N.hpp"
|
//FIXME replace the two following includes with <boost/md5.hpp> after it becomes mainstream.
|
||||||
|
#include <boost/uuid/detail/md5.hpp>
|
||||||
|
#include <boost/algorithm/hex.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -263,15 +268,103 @@ void AppConfig::set_defaults()
|
|||||||
erase("", "object_settings_size");
|
erase("", "object_settings_size");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
static std::string appconfig_md5_hash_line(const std::string_view data)
|
||||||
|
{
|
||||||
|
//FIXME replace the two following includes with <boost/md5.hpp> after it becomes mainstream.
|
||||||
|
// return boost::md5(data).hex_str_value();
|
||||||
|
// boost::uuids::detail::md5 is an internal namespace thus it may change in the future.
|
||||||
|
// Also this implementation is not the fastest, it was designed for short blocks of text.
|
||||||
|
using boost::uuids::detail::md5;
|
||||||
|
md5 md5_hash;
|
||||||
|
// unsigned int[4], 128 bits
|
||||||
|
md5::digest_type md5_digest{};
|
||||||
|
std::string md5_digest_str;
|
||||||
|
md5_hash.process_bytes(data.data(), data.size());
|
||||||
|
md5_hash.get_digest(md5_digest);
|
||||||
|
boost::algorithm::hex(md5_digest, md5_digest + std::size(md5_digest), std::back_inserter(md5_digest_str));
|
||||||
|
// MD5 hash is 32 HEX digits long.
|
||||||
|
assert(md5_digest_str.size() == 32);
|
||||||
|
// This line will be emited at the end of the file.
|
||||||
|
return "# MD5 checksum " + md5_digest_str + "\n";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assume that the last line with the comment inside the config file contains a checksum and that the user didn't modify the config file.
|
||||||
|
static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
|
||||||
|
{
|
||||||
|
auto read_whole_config_file = [&ifs]() -> std::string {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << ifs.rdbuf();
|
||||||
|
return ss.str();
|
||||||
|
};
|
||||||
|
|
||||||
|
ifs.seekg(0, boost::nowide::ifstream::beg);
|
||||||
|
std::string whole_config = read_whole_config_file();
|
||||||
|
|
||||||
|
// The checksum should be on the last line in the config file.
|
||||||
|
if (size_t last_comment_pos = whole_config.find_last_of('#'); last_comment_pos != std::string::npos) {
|
||||||
|
// Split read config into two parts, one with checksum, and the second part is part with configuration from the checksum was computed.
|
||||||
|
// Verify existence and validity of the MD5 checksum line at the end of the file.
|
||||||
|
// When the checksum isn't found, the checksum was not saved correctly, it was removed or it is an older config file without the checksum.
|
||||||
|
// If the checksum is incorrect, then the file was either not saved correctly or modified.
|
||||||
|
if (std::string_view(whole_config.c_str() + last_comment_pos, whole_config.size() - last_comment_pos) == appconfig_md5_hash_line({ whole_config.data(), last_comment_pos }))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
std::string AppConfig::load()
|
std::string AppConfig::load()
|
||||||
{
|
{
|
||||||
// 1) Read the complete config file into a boost::property_tree.
|
// 1) Read the complete config file into a boost::property_tree.
|
||||||
namespace pt = boost::property_tree;
|
namespace pt = boost::property_tree;
|
||||||
pt::ptree tree;
|
pt::ptree tree;
|
||||||
boost::nowide::ifstream ifs(AppConfig::config_path());
|
boost::nowide::ifstream ifs;
|
||||||
|
bool recovered = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ifs.open(AppConfig::config_path());
|
||||||
|
#ifdef WIN32
|
||||||
|
// Verify the checksum of the config file without taking just for debugging purpose.
|
||||||
|
if (!verify_config_file_checksum(ifs))
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "The configuration file " << AppConfig::config_path() <<
|
||||||
|
" has a wrong MD5 checksum or the checksum is missing. This may indicate a file corruption or a harmless user edit.";
|
||||||
|
|
||||||
|
ifs.seekg(0, boost::nowide::ifstream::beg);
|
||||||
|
#endif
|
||||||
pt::read_ini(ifs, tree);
|
pt::read_ini(ifs, tree);
|
||||||
} catch (pt::ptree_error& ex) {
|
} catch (pt::ptree_error& ex) {
|
||||||
|
#ifdef WIN32
|
||||||
|
// The configuration file is corrupted, try replacing it with the backup configuration.
|
||||||
|
ifs.close();
|
||||||
|
std::string backup_path = (boost::format("%1%.bak") % AppConfig::config_path()).str();
|
||||||
|
if (boost::filesystem::exists(backup_path)) {
|
||||||
|
// Compute checksum of the configuration backup file and try to load configuration from it when the checksum is correct.
|
||||||
|
boost::nowide::ifstream backup_ifs(backup_path);
|
||||||
|
if (!verify_config_file_checksum(backup_ifs)) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << format("Both \"%1%\" and \"%2%\" are corrupted. It isn't possible to restore configuration from the backup.", AppConfig::config_path(), backup_path);
|
||||||
|
backup_ifs.close();
|
||||||
|
boost::filesystem::remove(backup_path);
|
||||||
|
} else if (std::string error_message; copy_file(backup_path, AppConfig::config_path(), error_message, false) != SUCCESS) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << format("Configuration file \"%1%\" is corrupted. Failed to restore from backup \"%2%\": %3%", AppConfig::config_path(), backup_path, error_message);
|
||||||
|
backup_ifs.close();
|
||||||
|
boost::filesystem::remove(backup_path);
|
||||||
|
} else {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << format("Configuration file \"%1%\" was corrupted. It has been succesfully restored from the backup \"%2%\".", AppConfig::config_path(), backup_path);
|
||||||
|
// Try parse configuration file after restore from backup.
|
||||||
|
try {
|
||||||
|
ifs.open(AppConfig::config_path());
|
||||||
|
pt::read_ini(ifs, tree);
|
||||||
|
recovered = true;
|
||||||
|
} catch (pt::ptree_error& ex) {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\" after it has been restored from backup: %2%", AppConfig::config_path(), ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif // WIN32
|
||||||
|
BOOST_LOG_TRIVIAL(info) << format("Failed to parse configuration file \"%1%\": %2%", AppConfig::config_path(), ex.what());
|
||||||
|
if (! recovered) {
|
||||||
|
// Report the initial error of parsing PrusaSlicer.ini.
|
||||||
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
|
// Error while parsing config file. We'll customize the error message and rethrow to be displayed.
|
||||||
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
|
// ! But to avoid the use of _utf8 (related to use of wxWidgets)
|
||||||
// we will rethrow this exception from the place of load() call, if returned value wouldn't be empty
|
// we will rethrow this exception from the place of load() call, if returned value wouldn't be empty
|
||||||
@ -283,6 +376,7 @@ std::string AppConfig::load()
|
|||||||
*/
|
*/
|
||||||
return ex.what();
|
return ex.what();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 2) Parse the property_tree, extract the sections and key / value pairs.
|
// 2) Parse the property_tree, extract the sections and key / value pairs.
|
||||||
for (const auto §ion : tree) {
|
for (const auto §ion : tree) {
|
||||||
@ -358,22 +452,21 @@ void AppConfig::save()
|
|||||||
const auto path = config_path();
|
const auto path = config_path();
|
||||||
std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str();
|
std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str();
|
||||||
|
|
||||||
boost::nowide::ofstream c;
|
std::stringstream config_ss;
|
||||||
c.open(path_pid, std::ios::out | std::ios::trunc);
|
|
||||||
if (m_mode == EAppMode::Editor)
|
if (m_mode == EAppMode::Editor)
|
||||||
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
|
config_ss << "# " << Slic3r::header_slic3r_generated() << std::endl;
|
||||||
else
|
else
|
||||||
c << "# " << Slic3r::header_gcodeviewer_generated() << std::endl;
|
config_ss << "# " << Slic3r::header_gcodeviewer_generated() << std::endl;
|
||||||
// Make sure the "no" category is written first.
|
// Make sure the "no" category is written first.
|
||||||
for (const std::pair<std::string, std::string> &kvp : m_storage[""])
|
for (const auto& kvp : m_storage[""])
|
||||||
c << kvp.first << " = " << kvp.second << std::endl;
|
config_ss << kvp.first << " = " << kvp.second << std::endl;
|
||||||
// Write the other categories.
|
// Write the other categories.
|
||||||
for (const auto category : m_storage) {
|
for (const auto category : m_storage) {
|
||||||
if (category.first.empty())
|
if (category.first.empty())
|
||||||
continue;
|
continue;
|
||||||
c << std::endl << "[" << category.first << "]" << std::endl;
|
config_ss << std::endl << "[" << category.first << "]" << std::endl;
|
||||||
for (const std::pair<std::string, std::string> &kvp : category.second)
|
for (const auto& kvp : category.second)
|
||||||
c << kvp.first << " = " << kvp.second << std::endl;
|
config_ss << kvp.first << " = " << kvp.second << std::endl;
|
||||||
}
|
}
|
||||||
// Write vendor sections
|
// Write vendor sections
|
||||||
for (const auto &vendor : m_vendors) {
|
for (const auto &vendor : m_vendors) {
|
||||||
@ -381,17 +474,42 @@ void AppConfig::save()
|
|||||||
for (const auto &model : vendor.second) { size_sum += model.second.size(); }
|
for (const auto &model : vendor.second) { size_sum += model.second.size(); }
|
||||||
if (size_sum == 0) { continue; }
|
if (size_sum == 0) { continue; }
|
||||||
|
|
||||||
c << std::endl << "[" << VENDOR_PREFIX << vendor.first << "]" << std::endl;
|
config_ss << std::endl << "[" << VENDOR_PREFIX << vendor.first << "]" << std::endl;
|
||||||
|
|
||||||
for (const auto &model : vendor.second) {
|
for (const auto &model : vendor.second) {
|
||||||
if (model.second.size() == 0) { continue; }
|
if (model.second.empty()) { continue; }
|
||||||
const std::vector<std::string> variants(model.second.begin(), model.second.end());
|
const std::vector<std::string> variants(model.second.begin(), model.second.end());
|
||||||
const auto escaped = escape_strings_cstyle(variants);
|
const auto escaped = escape_strings_cstyle(variants);
|
||||||
c << MODEL_PREFIX << model.first << " = " << escaped << std::endl;
|
config_ss << MODEL_PREFIX << model.first << " = " << escaped << std::endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// One empty line before the MD5 sum.
|
||||||
|
config_ss << std::endl;
|
||||||
|
|
||||||
|
std::string config_str = config_ss.str();
|
||||||
|
boost::nowide::ofstream c;
|
||||||
|
c.open(path_pid, std::ios::out | std::ios::trunc);
|
||||||
|
c << config_str;
|
||||||
|
#ifdef WIN32
|
||||||
|
// WIN32 specific: The final "rename_file()" call is not safe in case of an application crash, there is no atomic "rename file" API
|
||||||
|
// provided by Windows (sic!). Therefore we save a MD5 checksum to be able to verify file corruption. In addition,
|
||||||
|
// we save the config file into a backup first before moving it to the final destination.
|
||||||
|
c << appconfig_md5_hash_line(config_str);
|
||||||
|
#endif
|
||||||
c.close();
|
c.close();
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
// Make a backup of the configuration file before copying it to the final destination.
|
||||||
|
std::string error_message;
|
||||||
|
std::string backup_path = (boost::format("%1%.bak") % path).str();
|
||||||
|
// Copy configuration file with PID suffix into the configuration file with "bak" suffix.
|
||||||
|
if (copy_file(path_pid, backup_path, error_message, false) != SUCCESS)
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Copying from " << path_pid << " to " << backup_path << " failed. Failed to create a backup configuration.";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Rename the config atomically.
|
||||||
|
// On Windows, the rename is likely NOT atomic, thus it may fail if PrusaSlicer crashes on another thread in the meanwhile.
|
||||||
|
// To cope with that, we already made a backup of the config on Windows.
|
||||||
rename_file(path_pid, path);
|
rename_file(path_pid, path);
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ public:
|
|||||||
|
|
||||||
// Load the slic3r.ini from a user profile directory (or a datadir, if configured).
|
// Load the slic3r.ini from a user profile directory (or a datadir, if configured).
|
||||||
// return error string or empty strinf
|
// return error string or empty strinf
|
||||||
std::string load();
|
std::string load();
|
||||||
// Store the slic3r.ini into a user profile directory (or a datadir, if configured).
|
// Store the slic3r.ini into a user profile directory (or a datadir, if configured).
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ add_library(libslic3r STATIC
|
|||||||
EdgeGrid.hpp
|
EdgeGrid.hpp
|
||||||
ElephantFootCompensation.cpp
|
ElephantFootCompensation.cpp
|
||||||
ElephantFootCompensation.hpp
|
ElephantFootCompensation.hpp
|
||||||
|
enum_bitmask.hpp
|
||||||
ExPolygon.cpp
|
ExPolygon.cpp
|
||||||
ExPolygon.hpp
|
ExPolygon.hpp
|
||||||
ExPolygonCollection.cpp
|
ExPolygonCollection.cpp
|
||||||
|
@ -21,6 +21,10 @@
|
|||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
//FIXME for GCodeFlavor and gcfMarlin (for forward-compatibility conversion)
|
||||||
|
// This is not nice, likely it would be better to pass the ConfigSubstitutionContext to handle_legacy().
|
||||||
|
#include "PrintConfig.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
|
||||||
@ -244,6 +248,10 @@ std::string escape_ampersand(const std::string& str)
|
|||||||
return std::string(out.data(), outptr - out.data());
|
return std::string(out.data(), outptr - out.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigOptionDeleter::operator()(ConfigOption* p) {
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> ConfigOptionDef::cli_args(const std::string &key) const
|
std::vector<std::string> ConfigOptionDef::cli_args(const std::string &key) const
|
||||||
{
|
{
|
||||||
std::vector<std::string> args;
|
std::vector<std::string> args;
|
||||||
@ -270,7 +278,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
|
|||||||
case coPercents: return new ConfigOptionPercentsNullable();
|
case coPercents: return new ConfigOptionPercentsNullable();
|
||||||
case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable();
|
case coFloatsOrPercents: return new ConfigOptionFloatsOrPercentsNullable();
|
||||||
case coBools: return new ConfigOptionBoolsNullable();
|
case coBools: return new ConfigOptionBoolsNullable();
|
||||||
default: throw Slic3r::RuntimeError(std::string("Unknown option type for nullable option ") + this->label);
|
default: throw ConfigurationError(std::string("Unknown option type for nullable option ") + this->label);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (this->type) {
|
switch (this->type) {
|
||||||
@ -291,7 +299,7 @@ ConfigOption* ConfigOptionDef::create_empty_option() const
|
|||||||
case coBool: return new ConfigOptionBool();
|
case coBool: return new ConfigOptionBool();
|
||||||
case coBools: return new ConfigOptionBools();
|
case coBools: return new ConfigOptionBools();
|
||||||
case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
|
case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map);
|
||||||
default: throw Slic3r::RuntimeError(std::string("Unknown option type for option ") + this->label);
|
default: throw ConfigurationError(std::string("Unknown option type for option ") + this->label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -394,7 +402,8 @@ std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, s
|
|||||||
|
|
||||||
// right: option description
|
// right: option description
|
||||||
std::string descr = def.tooltip;
|
std::string descr = def.tooltip;
|
||||||
if (show_defaults && def.default_value && def.type != coBool
|
bool show_defaults_this = show_defaults || def.opt_key == "config_compatibility";
|
||||||
|
if (show_defaults_this && def.default_value && def.type != coBool
|
||||||
&& (def.type != coString || !def.default_value->serialize().empty())) {
|
&& (def.type != coString || !def.default_value->serialize().empty())) {
|
||||||
descr += " (";
|
descr += " (";
|
||||||
if (!def.sidetext.empty()) {
|
if (!def.sidetext.empty()) {
|
||||||
@ -525,7 +534,7 @@ void ConfigBase::set(const std::string &opt_key, double value, bool create)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, bool append)
|
bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions_ctxt, bool append)
|
||||||
{
|
{
|
||||||
t_config_option_key opt_key = opt_key_src;
|
t_config_option_key opt_key = opt_key_src;
|
||||||
std::string value = value_src;
|
std::string value = value_src;
|
||||||
@ -535,23 +544,22 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src,
|
|||||||
if (opt_key.empty())
|
if (opt_key.empty())
|
||||||
// Ignore the option.
|
// Ignore the option.
|
||||||
return true;
|
return true;
|
||||||
return this->set_deserialize_raw(opt_key, value, append);
|
return this->set_deserialize_raw(opt_key, value, substitutions_ctxt, append);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase::set_deserialize(const t_config_option_key& opt_key_src, const std::string& value_src, bool append)
|
void ConfigBase::set_deserialize(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions_ctxt, bool append)
|
||||||
{
|
{
|
||||||
if (!this->set_deserialize_nothrow(opt_key_src, value_src, append)) {
|
if (! this->set_deserialize_nothrow(opt_key_src, value_src, substitutions_ctxt, append))
|
||||||
throw BadOptionTypeException(format("ConfigBase::set_deserialize() failed for parameter \"%1%\", value \"%2%\"", opt_key_src, value_src));
|
throw BadOptionValueException(format("Invalid value provided for parameter %1%: %2%", opt_key_src, value_src));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase::set_deserialize(std::initializer_list<SetDeserializeItem> items)
|
void ConfigBase::set_deserialize(std::initializer_list<SetDeserializeItem> items, ConfigSubstitutionContext& substitutions_ctxt)
|
||||||
{
|
{
|
||||||
for (const SetDeserializeItem &item : items)
|
for (const SetDeserializeItem &item : items)
|
||||||
this->set_deserialize(item.opt_key, item.opt_value, item.append);
|
this->set_deserialize(item.opt_key, item.opt_value, substitutions_ctxt, item.append);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, bool append)
|
bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &value, ConfigSubstitutionContext& substitutions_ctxt, bool append)
|
||||||
{
|
{
|
||||||
t_config_option_key opt_key = opt_key_src;
|
t_config_option_key opt_key = opt_key_src;
|
||||||
// Try to deserialize the option by its name.
|
// Try to deserialize the option by its name.
|
||||||
@ -580,7 +588,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
|
|||||||
// Aliasing for example "solid_layers" to "top_solid_layers" and "bottom_solid_layers".
|
// Aliasing for example "solid_layers" to "top_solid_layers" and "bottom_solid_layers".
|
||||||
for (const t_config_option_key &shortcut : optdef->shortcut)
|
for (const t_config_option_key &shortcut : optdef->shortcut)
|
||||||
// Recursive call.
|
// Recursive call.
|
||||||
if (! this->set_deserialize_raw(shortcut, value, append))
|
if (! this->set_deserialize_raw(shortcut, value, substitutions_ctxt, append))
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -588,10 +596,56 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
|
|||||||
ConfigOption *opt = this->option(opt_key, true);
|
ConfigOption *opt = this->option(opt_key, true);
|
||||||
if (opt == nullptr)
|
if (opt == nullptr)
|
||||||
throw new UnknownOptionException(opt_key);
|
throw new UnknownOptionException(opt_key);
|
||||||
|
bool success = true;
|
||||||
|
if (!optdef->can_phony || !value.empty()) {
|
||||||
|
success = true;
|
||||||
|
bool substituted = false;
|
||||||
|
if (optdef->type == coBools && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable) {
|
||||||
|
//FIXME Special handling of vectors of bools, quick and not so dirty solution before PrusaSlicer 2.3.2 release.
|
||||||
|
bool nullable = opt->nullable();
|
||||||
|
ConfigHelpers::DeserializationSubstitution default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToFalse;
|
||||||
|
if (optdef->default_value) {
|
||||||
|
// Default value for vectors of booleans used in a "per extruder" context, thus the default contains just a single value.
|
||||||
|
assert(dynamic_cast<const ConfigOptionVector<unsigned char>*>(optdef->default_value.get()));
|
||||||
|
auto &values = static_cast<const ConfigOptionVector<unsigned char>*>(optdef->default_value.get())->values;
|
||||||
|
if (values.size() == 1 && values.front() == 1)
|
||||||
|
default_value = ConfigHelpers::DeserializationSubstitution::DefaultsToTrue;
|
||||||
|
}
|
||||||
|
auto result = nullable ?
|
||||||
|
static_cast<ConfigOptionBoolsNullable*>(opt)->deserialize_with_substitutions(value, append, default_value) :
|
||||||
|
static_cast<ConfigOptionBools*>(opt)->deserialize_with_substitutions(value, append, default_value);
|
||||||
|
success = result != ConfigHelpers::DeserializationResult::Failed;
|
||||||
|
substituted = result == ConfigHelpers::DeserializationResult::Substituted;
|
||||||
|
} else {
|
||||||
|
success = opt->deserialize(value, append);
|
||||||
|
if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable &&
|
||||||
|
// Only allow substitutions of an enum value by another enum value or a boolean value with an enum value.
|
||||||
|
// That means, we expect enum values being added in the future and possibly booleans being converted to enums.
|
||||||
|
(optdef->type == coEnum || optdef->type == coBool) && ConfigHelpers::looks_like_enum_value(value)) {
|
||||||
|
// Deserialize failed, try to substitute with a default value.
|
||||||
|
assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||||
|
if (optdef->type == coEnum && opt_key == "gcode_flavor" && (value == "marlin2" || value == "marlinfirmware"))
|
||||||
|
static_cast<ConfigOptionEnum<GCodeFlavor>*>(opt)->value = gcfMarlin;
|
||||||
|
else if (optdef->type == coBool)
|
||||||
|
static_cast<ConfigOptionBool*>(opt)->value = ConfigHelpers::enum_looks_like_true_value(value);
|
||||||
|
else
|
||||||
|
// Just use the default of the option.
|
||||||
|
opt->set(optdef->default_value.get());
|
||||||
|
success = true;
|
||||||
|
substituted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ok = true;
|
if (substituted && (substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable ||
|
||||||
if (!optdef->can_phony || !value.empty())
|
substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)) {
|
||||||
ok = opt->deserialize(value, append);
|
// Log the substitution.
|
||||||
|
ConfigSubstitution config_substitution;
|
||||||
|
config_substitution.opt_def = optdef;
|
||||||
|
config_substitution.old_value = value;
|
||||||
|
config_substitution.new_value = ConfigOptionUniquePtr(opt->clone());
|
||||||
|
substitutions_ctxt.substitutions.emplace_back(std::move(config_substitution));
|
||||||
|
}
|
||||||
|
}
|
||||||
//set phony status
|
//set phony status
|
||||||
if (optdef->can_phony)
|
if (optdef->can_phony)
|
||||||
if(value.empty())
|
if(value.empty())
|
||||||
@ -600,8 +654,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
|
|||||||
opt->set_phony(false);
|
opt->set_phony(false);
|
||||||
else
|
else
|
||||||
opt->set_phony(false);
|
opt->set_phony(false);
|
||||||
|
return success;
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an absolute value of a possibly relative config variable.
|
// Return an absolute value of a possibly relative config variable.
|
||||||
@ -662,7 +715,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
|
|||||||
cast_opt->get_abs_value(this->get_abs_value(opt_def->ratio_over));
|
cast_opt->get_abs_value(this->get_abs_value(opt_def->ratio_over));
|
||||||
}
|
}
|
||||||
std::stringstream ss; ss << "ConfigBase::get_abs_value(): "<< opt_key<<" has not a valid option type for get_abs_value()";
|
std::stringstream ss; ss << "ConfigBase::get_abs_value(): "<< opt_key<<" has not a valid option type for get_abs_value()";
|
||||||
throw Slic3r::RuntimeError(ss.str());
|
throw ConfigurationError(ss.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return an absolute value of a possibly relative config variable.
|
// Return an absolute value of a possibly relative config variable.
|
||||||
@ -673,7 +726,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double rati
|
|||||||
const ConfigOption *raw_opt = this->option(opt_key);
|
const ConfigOption *raw_opt = this->option(opt_key);
|
||||||
assert(raw_opt != nullptr);
|
assert(raw_opt != nullptr);
|
||||||
if (raw_opt->type() != coFloatOrPercent)
|
if (raw_opt->type() != coFloatOrPercent)
|
||||||
throw Slic3r::RuntimeError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
|
throw ConfigurationError("ConfigBase::get_abs_value(): opt_key is not of coFloatOrPercent");
|
||||||
// Compute absolute value.
|
// Compute absolute value.
|
||||||
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over);
|
return static_cast<const ConfigOptionFloatOrPercent*>(raw_opt)->get_abs_value(ratio_over);
|
||||||
}
|
}
|
||||||
@ -696,67 +749,78 @@ void ConfigBase::setenv_() const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase::load(const std::string &file)
|
ConfigSubstitutions ConfigBase::load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
if (is_gcode_file(file))
|
return is_gcode_file(file) ?
|
||||||
this->load_from_gcode_file(file);
|
this->load_from_gcode_file(file, compatibility_rule) :
|
||||||
else
|
this->load_from_ini(file, compatibility_rule);
|
||||||
this->load_from_ini(file);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase::load_from_ini(const std::string &file)
|
ConfigSubstitutions ConfigBase::load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
boost::property_tree::ptree tree;
|
boost::property_tree::ptree tree;
|
||||||
boost::nowide::ifstream ifs(file);
|
boost::nowide::ifstream ifs(file);
|
||||||
boost::property_tree::read_ini(ifs, tree);
|
boost::property_tree::read_ini(ifs, tree);
|
||||||
this->load(tree);
|
return this->load(tree, compatibility_rule);
|
||||||
|
} catch (const ConfigurationError &e) {
|
||||||
|
throw ConfigurationError(format("Failed loading configuration file \"%1%\": %2%", file, e.what()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase::load(const boost::property_tree::ptree &tree)
|
ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
|
ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
|
||||||
for (const boost::property_tree::ptree::value_type &v : tree) {
|
for (const boost::property_tree::ptree::value_type &v : tree) {
|
||||||
try {
|
try {
|
||||||
t_config_option_key opt_key = v.first;
|
t_config_option_key opt_key = v.first;
|
||||||
this->set_deserialize(opt_key, v.second.get_value<std::string>());
|
this->set_deserialize(opt_key, v.second.get_value<std::string>(), substitutions_ctxt);
|
||||||
} catch (UnknownOptionException & /* e */) {
|
} catch (UnknownOptionException & /* e */) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return std::move(substitutions_ctxt.substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the config keys from the tail of a G-code file.
|
// Load the config keys from the tail of a G-code file.
|
||||||
void ConfigBase::load_from_gcode_file(const std::string &file)
|
ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
// Read a 64k block from the end of the G-code.
|
// Read a 64k block from the end of the G-code.
|
||||||
boost::nowide::ifstream ifs(file);
|
boost::nowide::ifstream ifs(file);
|
||||||
{
|
{
|
||||||
const char slic3r_gcode_header[] = "; generated by Slic3r ";
|
const char slic3r_gcode_header[] = "; generated by Slic3r ";
|
||||||
const char slic3rpp_gcode_header[] = "; generated by Slic3r++ ";
|
const char slic3rpp_gcode_header[] = "; generated by Slic3r++ ";
|
||||||
const char superslicer_gcode_header[] = "; generated by SuperSlicer ";
|
const char superslicer_gcode_header[] = "; generated by SuperSlicer ";
|
||||||
const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer ";
|
const char prusaslicer_gcode_header[] = "; generated by PrusaSlicer ";
|
||||||
std::string firstline;
|
std::string firstline;
|
||||||
std::getline(ifs, firstline);
|
std::getline(ifs, firstline);
|
||||||
if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 &&
|
if (strncmp(slic3r_gcode_header, firstline.c_str(), strlen(slic3r_gcode_header)) != 0 &&
|
||||||
strncmp(slic3rpp_gcode_header, firstline.c_str(), strlen(slic3rpp_gcode_header)) != 0 &&
|
strncmp(slic3rpp_gcode_header, firstline.c_str(), strlen(slic3rpp_gcode_header)) != 0 &&
|
||||||
strncmp(superslicer_gcode_header, firstline.c_str(), strlen(superslicer_gcode_header)) != 0 &&
|
strncmp(superslicer_gcode_header, firstline.c_str(), strlen(superslicer_gcode_header)) != 0 &&
|
||||||
strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0)
|
strncmp(prusaslicer_gcode_header, firstline.c_str(), strlen(prusaslicer_gcode_header)) != 0)
|
||||||
throw Slic3r::RuntimeError("Not a g-code recognized for configuration import.");
|
throw ConfigurationError("Not a g-code recognized for configuration import.");
|
||||||
}
|
}
|
||||||
ifs.seekg(0, ifs.end);
|
ifs.seekg(0, ifs.end);
|
||||||
auto file_length = ifs.tellg();
|
auto file_length = ifs.tellg();
|
||||||
auto data_length = std::min<std::fstream::pos_type>(65535, file_length);
|
auto data_length = std::min<std::fstream::pos_type>(65535, file_length);
|
||||||
ifs.seekg(file_length - data_length, ifs.beg);
|
ifs.seekg(file_length - data_length, ifs.beg);
|
||||||
std::vector<char> data(size_t(data_length) + 1, 0);
|
std::vector<char> data(size_t(data_length) + 1, 0);
|
||||||
ifs.read(data.data(), data_length);
|
ifs.read(data.data(), data_length);
|
||||||
ifs.close();
|
ifs.close();
|
||||||
|
|
||||||
size_t key_value_pairs = load_from_gcode_string(data.data());
|
ConfigSubstitutionContext substitutions_ctxt(compatibility_rule);
|
||||||
|
size_t key_value_pairs = load_from_gcode_string(data.data(), substitutions_ctxt);
|
||||||
if (key_value_pairs < 80)
|
if (key_value_pairs < 80)
|
||||||
throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
|
throw ConfigurationError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
|
||||||
|
return std::move(substitutions_ctxt.substitutions);
|
||||||
|
} catch (const ConfigurationError &e) {
|
||||||
|
throw ConfigurationError(format("Failed loading configuration from G-code \"%1%\": %2%", file, e.what()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the config keys from the given string.
|
// Load the config keys from the given string.
|
||||||
size_t ConfigBase::load_from_gcode_string(const char* str)
|
size_t ConfigBase::load_from_gcode_string(const char* str, ConfigSubstitutionContext& substitutions)
|
||||||
{
|
{
|
||||||
if (str == nullptr)
|
if (str == nullptr)
|
||||||
return 0;
|
return 0;
|
||||||
@ -801,8 +865,7 @@ size_t ConfigBase::load_from_gcode_string(const char* str)
|
|||||||
if (key == nullptr)
|
if (key == nullptr)
|
||||||
break;
|
break;
|
||||||
try {
|
try {
|
||||||
//change it from set_deserialize to set_deserialize_nothrow to allow bad/old config to swtch to default value.
|
this->set_deserialize(std::string(key, key_end), std::string(value, end), substitutions);
|
||||||
if(this->set_deserialize_nothrow(std::string(key, key_end), std::string(value, end)))
|
|
||||||
++num_key_value_pairs;
|
++num_key_value_pairs;
|
||||||
}
|
}
|
||||||
catch (UnknownOptionException & /* e */) {
|
catch (UnknownOptionException & /* e */) {
|
||||||
@ -839,7 +902,7 @@ void ConfigBase::null_nullables()
|
|||||||
ConfigOption *opt = this->optptr(opt_key, false);
|
ConfigOption *opt = this->optptr(opt_key, false);
|
||||||
assert(opt != nullptr);
|
assert(opt != nullptr);
|
||||||
if (opt->nullable())
|
if (opt->nullable())
|
||||||
opt->deserialize("nil");
|
opt->deserialize("nil", ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -890,7 +953,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
|
|||||||
throw NoDefinitionException(opt_key);
|
throw NoDefinitionException(opt_key);
|
||||||
const ConfigOptionDef *optdef = def->get(opt_key);
|
const ConfigOptionDef *optdef = def->get(opt_key);
|
||||||
if (optdef == nullptr)
|
if (optdef == nullptr)
|
||||||
// throw Slic3r::RuntimeError(std::string("Invalid option name: ") + opt_key);
|
// throw ConfigurationError(std::string("Invalid option name: ") + opt_key);
|
||||||
// Let the parent decide what to do if the opt_key is not defined by this->def().
|
// Let the parent decide what to do if the opt_key is not defined by this->def().
|
||||||
return nullptr;
|
return nullptr;
|
||||||
ConfigOption *opt = optdef->create_default_option();
|
ConfigOption *opt = optdef->create_default_option();
|
||||||
@ -1010,8 +1073,10 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
|
|||||||
// Do not unescape single string values, the unescaping is left to the calling shell.
|
// Do not unescape single string values, the unescaping is left to the calling shell.
|
||||||
static_cast<ConfigOptionString*>(opt_base)->value = value;
|
static_cast<ConfigOptionString*>(opt_base)->value = value;
|
||||||
} else {
|
} else {
|
||||||
|
// Just bail out if the configuration value is not understood.
|
||||||
|
ConfigSubstitutionContext context(ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
// Any scalar value of a type different from Bool and String.
|
// Any scalar value of a type different from Bool and String.
|
||||||
if (! this->set_deserialize_nothrow(opt_key, value, false)) {
|
if (! this->set_deserialize_nothrow(opt_key, value, context, false)) {
|
||||||
boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
|
boost::nowide::cerr << "Invalid value supplied for --" << token.c_str() << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include "Exception.hpp"
|
#include "Exception.hpp"
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
#include <boost/algorithm/string/trim.hpp>
|
||||||
#include <boost/format/format_fwd.hpp>
|
#include <boost/format/format_fwd.hpp>
|
||||||
#include <boost/property_tree/ptree_fwd.hpp>
|
#include <boost/property_tree/ptree_fwd.hpp>
|
||||||
@ -75,23 +76,59 @@ enum OptionCategory : int
|
|||||||
};
|
};
|
||||||
std::string toString(OptionCategory opt);
|
std::string toString(OptionCategory opt);
|
||||||
|
|
||||||
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
|
namespace ConfigHelpers {
|
||||||
class UnknownOptionException : public Slic3r::RuntimeError {
|
inline bool looks_like_enum_value(std::string value)
|
||||||
public:
|
{
|
||||||
UnknownOptionException() :
|
boost::trim(value);
|
||||||
Slic3r::RuntimeError("Unknown option exception") {}
|
if (value.empty() || value.size() > 64 || ! isalpha(value.front()))
|
||||||
UnknownOptionException(const std::string &opt_key) :
|
return false;
|
||||||
Slic3r::RuntimeError(std::string("Unknown option exception: ") + opt_key) {}
|
for (const char c : value)
|
||||||
|
if (! (isalnum(c) || c == '_' || c == '-'))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool enum_looks_like_true_value(std::string value) {
|
||||||
|
boost::trim(value);
|
||||||
|
return boost::iequals(value, "enabled") || boost::iequals(value, "on");
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class DeserializationSubstitution {
|
||||||
|
Disabled,
|
||||||
|
DefaultsToFalse,
|
||||||
|
DefaultsToTrue
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DeserializationResult {
|
||||||
|
Loaded,
|
||||||
|
Substituted,
|
||||||
|
Failed,
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
|
// Base for all exceptions thrown by the configuration layer.
|
||||||
class NoDefinitionException : public Slic3r::RuntimeError
|
class ConfigurationError : public Slic3r::RuntimeError {
|
||||||
|
public:
|
||||||
|
using RuntimeError::RuntimeError;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialization of std::exception to indicate that an unknown config option has been encountered.
|
||||||
|
class UnknownOptionException : public ConfigurationError {
|
||||||
|
public:
|
||||||
|
UnknownOptionException() :
|
||||||
|
ConfigurationError("Unknown option exception") {}
|
||||||
|
UnknownOptionException(const std::string &opt_key) :
|
||||||
|
ConfigurationError(std::string("Unknown option exception: ") + opt_key) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
|
||||||
|
class NoDefinitionException : public ConfigurationError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NoDefinitionException() :
|
NoDefinitionException() :
|
||||||
Slic3r::RuntimeError("No definition exception") {}
|
ConfigurationError("No definition exception") {}
|
||||||
NoDefinitionException(const std::string &opt_key) :
|
NoDefinitionException(const std::string &opt_key) :
|
||||||
Slic3r::RuntimeError(std::string("No definition exception: ") + opt_key) {}
|
ConfigurationError(std::string("No definition exception: ") + opt_key) {}
|
||||||
};
|
};
|
||||||
// a bit more specific than a runtime_error
|
// a bit more specific than a runtime_error
|
||||||
class ConfigurationException : public std::runtime_error
|
class ConfigurationException : public std::runtime_error
|
||||||
@ -103,13 +140,22 @@ public:
|
|||||||
std::runtime_error(std::string("Configuration exception: ") + opt_key) {}
|
std::runtime_error(std::string("Configuration exception: ") + opt_key) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Indicate that an unsupported accessor was called on a config option.
|
// Indicate that an unsupported accessor was called on a config option.
|
||||||
class BadOptionTypeException : public Slic3r::RuntimeError
|
class BadOptionTypeException : public ConfigurationError
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BadOptionTypeException() : Slic3r::RuntimeError("Bad option type exception") {}
|
BadOptionTypeException() : ConfigurationError("Bad option type exception") {}
|
||||||
BadOptionTypeException(const std::string &message) : Slic3r::RuntimeError(message) {}
|
BadOptionTypeException(const std::string &message) : ConfigurationError(message) {}
|
||||||
BadOptionTypeException(const char* message) : Slic3r::RuntimeError(message) {}
|
BadOptionTypeException(const char* message) : ConfigurationError(message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Indicate that an option has been deserialized from an invalid value.
|
||||||
|
class BadOptionValueException : public ConfigurationError
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BadOptionValueException() : ConfigurationError("Bad option value exception") {}
|
||||||
|
BadOptionValueException(const std::string &message) : ConfigurationError(message) {}
|
||||||
|
BadOptionValueException(const char* message) : ConfigurationError(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Type of a configuration value.
|
// Type of a configuration value.
|
||||||
@ -208,8 +254,47 @@ inline OutputFormat operator&=(OutputFormat& a, OutputFormat b) {
|
|||||||
a = a & b; return a;
|
a = a & b; return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ForwardCompatibilitySubstitutionRule
|
||||||
|
{
|
||||||
|
// Disable susbtitution, throw exception if an option value is not recognized.
|
||||||
|
Disable,
|
||||||
|
// Enable substitution of an unknown option value with default. Log the substitution.
|
||||||
|
Enable,
|
||||||
|
// Enable substitution of an unknown option value with default. Don't log the substitution.
|
||||||
|
EnableSilent,
|
||||||
|
// Enable substitution of an unknown option value with default. Log substitutions in user profiles, don't log substitutions in system profiles.
|
||||||
|
EnableSystemSilent,
|
||||||
|
// Enable silent substitution of an unknown option value with default when loading user profiles. Throw on an unknown option value in a system profile.
|
||||||
|
EnableSilentDisableSystem,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConfigOption;
|
||||||
|
class ConfigOptionDef;
|
||||||
|
// For forward definition of ConfigOption in ConfigOptionUniquePtr, we have to define a custom deleter.
|
||||||
|
struct ConfigOptionDeleter { void operator()(ConfigOption* p); };
|
||||||
|
using ConfigOptionUniquePtr = std::unique_ptr<ConfigOption, ConfigOptionDeleter>;
|
||||||
|
|
||||||
|
// When parsing a configuration value, if the old_value is not understood by this PrusaSlicer version,
|
||||||
|
// it is being substituted with some default value that this PrusaSlicer could work with.
|
||||||
|
// This structure serves to inform the user about the substitutions having been done during file import.
|
||||||
|
struct ConfigSubstitution {
|
||||||
|
const ConfigOptionDef *opt_def { nullptr };
|
||||||
|
std::string old_value;
|
||||||
|
ConfigOptionUniquePtr new_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ConfigSubstitutions = std::vector<ConfigSubstitution>;
|
||||||
|
|
||||||
|
// Filled in by ConfigBase::set_deserialize_raw(), which based on "rule" either bails out
|
||||||
|
// or performs substitutions when encountering an unknown configuration value.
|
||||||
|
struct ConfigSubstitutionContext
|
||||||
|
{
|
||||||
|
ConfigSubstitutionContext(ForwardCompatibilitySubstitutionRule rl) : rule(rl) {}
|
||||||
|
bool empty() const throw() { return substitutions.empty(); }
|
||||||
|
|
||||||
|
ForwardCompatibilitySubstitutionRule rule;
|
||||||
|
ConfigSubstitutions substitutions;
|
||||||
|
};
|
||||||
|
|
||||||
// A generic value of a configuration option.
|
// A generic value of a configuration option.
|
||||||
class ConfigOption {
|
class ConfigOption {
|
||||||
@ -277,7 +362,7 @@ public:
|
|||||||
void set(const ConfigOption *rhs) override
|
void set(const ConfigOption *rhs) override
|
||||||
{
|
{
|
||||||
if (rhs->type() != this->type())
|
if (rhs->type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionSingle: Assigning an incompatible type");
|
throw ConfigurationError("ConfigOptionSingle: Assigning an incompatible type");
|
||||||
assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs));
|
assert(dynamic_cast<const ConfigOptionSingle<T>*>(rhs));
|
||||||
this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
|
this->value = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
|
||||||
this->phony = rhs->phony;
|
this->phony = rhs->phony;
|
||||||
@ -286,7 +371,7 @@ public:
|
|||||||
bool operator==(const ConfigOption &rhs) const override
|
bool operator==(const ConfigOption &rhs) const override
|
||||||
{
|
{
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionSingle: Comparing incompatible types");
|
throw ConfigurationError("ConfigOptionSingle: Comparing incompatible types");
|
||||||
assert(dynamic_cast<const ConfigOptionSingle<T>*>(&rhs));
|
assert(dynamic_cast<const ConfigOptionSingle<T>*>(&rhs));
|
||||||
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
|
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
|
||||||
}
|
}
|
||||||
@ -352,7 +437,7 @@ public:
|
|||||||
void set(const ConfigOption *rhs) override
|
void set(const ConfigOption *rhs) override
|
||||||
{
|
{
|
||||||
if (rhs->type() != this->type())
|
if (rhs->type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector: Assigning an incompatible type");
|
throw ConfigurationError("ConfigOptionVector: Assigning an incompatible type");
|
||||||
assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs));
|
assert(dynamic_cast<const ConfigOptionVector<T>*>(rhs));
|
||||||
this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
|
this->values = static_cast<const ConfigOptionVector<T>*>(rhs)->values;
|
||||||
this->phony = rhs->phony;
|
this->phony = rhs->phony;
|
||||||
@ -370,12 +455,12 @@ public:
|
|||||||
if (opt->type() == this->type()) {
|
if (opt->type() == this->type()) {
|
||||||
auto other = static_cast<const ConfigOptionVector<T>*>(opt);
|
auto other = static_cast<const ConfigOptionVector<T>*>(opt);
|
||||||
if (other->values.empty())
|
if (other->values.empty())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector::set(): Assigning from an empty vector");
|
throw ConfigurationError("ConfigOptionVector::set(): Assigning from an empty vector");
|
||||||
this->values.emplace_back(other->values.front());
|
this->values.emplace_back(other->values.front());
|
||||||
} else if (opt->type() == this->scalar_type())
|
} else if (opt->type() == this->scalar_type())
|
||||||
this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value);
|
this->values.emplace_back(static_cast<const ConfigOptionSingle<T>*>(opt)->value);
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector::set():: Assigning an incompatible type");
|
throw ConfigurationError("ConfigOptionVector::set():: Assigning an incompatible type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,12 +479,12 @@ public:
|
|||||||
// Assign the first value of the rhs vector.
|
// Assign the first value of the rhs vector.
|
||||||
auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
|
auto other = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||||
if (other->values.empty())
|
if (other->values.empty())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning from an empty vector");
|
throw ConfigurationError("ConfigOptionVector::set_at(): Assigning from an empty vector");
|
||||||
this->values[i] = other->get_at(j);
|
this->values[i] = other->get_at(j);
|
||||||
} else if (rhs->type() == this->scalar_type())
|
} else if (rhs->type() == this->scalar_type())
|
||||||
this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
|
this->values[i] = static_cast<const ConfigOptionSingle<T>*>(rhs)->value;
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector::set_at(): Assigning an incompatible type");
|
throw ConfigurationError("ConfigOptionVector::set_at(): Assigning an incompatible type");
|
||||||
}
|
}
|
||||||
|
|
||||||
const T& get_at(size_t i) const
|
const T& get_at(size_t i) const
|
||||||
@ -426,7 +511,7 @@ public:
|
|||||||
if (opt_default == nullptr)
|
if (opt_default == nullptr)
|
||||||
this->values.resize(n, this->default_value);
|
this->values.resize(n, this->default_value);
|
||||||
if (opt_default->type() != this->type())
|
if (opt_default->type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector::resize(): Extending with an incompatible type.");
|
throw ConfigurationError("ConfigOptionVector::resize(): Extending with an incompatible type.");
|
||||||
if(static_cast<const ConfigOptionVector<T>*>(opt_default)->values.empty())
|
if(static_cast<const ConfigOptionVector<T>*>(opt_default)->values.empty())
|
||||||
this->values.resize(n, this->default_value);
|
this->values.resize(n, this->default_value);
|
||||||
else
|
else
|
||||||
@ -446,7 +531,7 @@ public:
|
|||||||
bool operator==(const ConfigOption &rhs) const override
|
bool operator==(const ConfigOption &rhs) const override
|
||||||
{
|
{
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector: Comparing incompatible types");
|
throw ConfigurationError("ConfigOptionVector: Comparing incompatible types");
|
||||||
assert(dynamic_cast<const ConfigOptionVector<T>*>(&rhs));
|
assert(dynamic_cast<const ConfigOptionVector<T>*>(&rhs));
|
||||||
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
|
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
|
||||||
}
|
}
|
||||||
@ -458,9 +543,9 @@ public:
|
|||||||
// An option overrides another option if it is not nil and not equal.
|
// An option overrides another option if it is not nil and not equal.
|
||||||
bool overriden_by(const ConfigOption *rhs) const override {
|
bool overriden_by(const ConfigOption *rhs) const override {
|
||||||
if (this->nullable())
|
if (this->nullable())
|
||||||
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption.");
|
throw ConfigurationError("Cannot override a nullable ConfigOption.");
|
||||||
if (rhs->type() != this->type())
|
if (rhs->type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector.overriden_by() applied to different types.");
|
throw ConfigurationError("ConfigOptionVector.overriden_by() applied to different types.");
|
||||||
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
|
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||||
if (! rhs->nullable())
|
if (! rhs->nullable())
|
||||||
// Overridding a non-nullable object with another non-nullable object.
|
// Overridding a non-nullable object with another non-nullable object.
|
||||||
@ -478,9 +563,9 @@ public:
|
|||||||
// Apply an override option, possibly a nullable one.
|
// Apply an override option, possibly a nullable one.
|
||||||
bool apply_override(const ConfigOption *rhs) override {
|
bool apply_override(const ConfigOption *rhs) override {
|
||||||
if (this->nullable())
|
if (this->nullable())
|
||||||
throw Slic3r::RuntimeError("Cannot override a nullable ConfigOption.");
|
throw ConfigurationError("Cannot override a nullable ConfigOption.");
|
||||||
if (rhs->type() != this->type())
|
if (rhs->type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionVector.apply_override() applied to different types.");
|
throw ConfigurationError("ConfigOptionVector.apply_override() applied to different types.");
|
||||||
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
|
auto rhs_vec = static_cast<const ConfigOptionVector<T>*>(rhs);
|
||||||
if (! rhs->nullable()) {
|
if (! rhs->nullable()) {
|
||||||
// Overridding a non-nullable object with another non-nullable object.
|
// Overridding a non-nullable object with another non-nullable object.
|
||||||
@ -571,7 +656,7 @@ public:
|
|||||||
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
||||||
bool operator==(const ConfigOption &rhs) const override {
|
bool operator==(const ConfigOption &rhs) const override {
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types");
|
throw ConfigurationError("ConfigOptionFloatsTempl: Comparing incompatible types");
|
||||||
assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs));
|
assert(dynamic_cast<const ConfigOptionVector<double>*>(&rhs));
|
||||||
return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values);
|
return vectors_equal(this->values, static_cast<const ConfigOptionVector<double>*>(&rhs)->values);
|
||||||
}
|
}
|
||||||
@ -618,7 +703,7 @@ public:
|
|||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
this->values.push_back(nil_value());
|
this->values.push_back(nil_value());
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
throw ConfigurationError("Deserializing nil into a non-nullable object");
|
||||||
} else {
|
} else {
|
||||||
std::istringstream iss(item_str);
|
std::istringstream iss(item_str);
|
||||||
double value;
|
double value;
|
||||||
@ -643,9 +728,9 @@ protected:
|
|||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
ss << "nil";
|
ss << "nil";
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Serializing NaN");
|
throw ConfigurationError("Serializing NaN");
|
||||||
} else
|
} else
|
||||||
throw Slic3r::RuntimeError("Serializing invalid number");
|
throw ConfigurationError("Serializing invalid number");
|
||||||
}
|
}
|
||||||
static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
|
static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
|
||||||
if (NULLABLE) {
|
if (NULLABLE) {
|
||||||
@ -764,7 +849,7 @@ public:
|
|||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
this->values.push_back(nil_value());
|
this->values.push_back(nil_value());
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
throw ConfigurationError("Deserializing nil into a non-nullable object");
|
||||||
} else {
|
} else {
|
||||||
std::istringstream iss(item_str);
|
std::istringstream iss(item_str);
|
||||||
int32_t value;
|
int32_t value;
|
||||||
@ -781,7 +866,7 @@ private:
|
|||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
ss << "nil";
|
ss << "nil";
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Serializing NaN");
|
throw ConfigurationError("Serializing NaN");
|
||||||
} else
|
} else
|
||||||
ss << v;
|
ss << v;
|
||||||
}
|
}
|
||||||
@ -970,7 +1055,7 @@ public:
|
|||||||
bool operator==(const ConfigOption &rhs) const override
|
bool operator==(const ConfigOption &rhs) const override
|
||||||
{
|
{
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionFloatOrPercent: Comparing incompatible types");
|
throw ConfigurationError("ConfigOptionFloatOrPercent: Comparing incompatible types");
|
||||||
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
|
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
|
||||||
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
|
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
|
||||||
}
|
}
|
||||||
@ -981,7 +1066,7 @@ public:
|
|||||||
|
|
||||||
void set(const ConfigOption *rhs) override {
|
void set(const ConfigOption *rhs) override {
|
||||||
if (rhs->type() != this->type())
|
if (rhs->type() != this->type())
|
||||||
throw ConfigurationException("ConfigOptionFloatOrPercent: Assigning an incompatible type");
|
throw ConfigurationError("ConfigOptionFloatOrPercent: Assigning an incompatible type");
|
||||||
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs));
|
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(rhs));
|
||||||
*this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs);
|
*this = *static_cast<const ConfigOptionFloatOrPercent*>(rhs);
|
||||||
}
|
}
|
||||||
@ -1046,7 +1131,7 @@ public:
|
|||||||
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
||||||
bool operator==(const ConfigOption &rhs) const override {
|
bool operator==(const ConfigOption &rhs) const override {
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
|
throw ConfigurationError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
|
||||||
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
|
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
|
||||||
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
|
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
|
||||||
}
|
}
|
||||||
@ -1093,7 +1178,7 @@ public:
|
|||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
this->values.push_back(nil_value());
|
this->values.push_back(nil_value());
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
throw ConfigurationError("Deserializing nil into a non-nullable object");
|
||||||
} else {
|
} else {
|
||||||
bool percent = item_str.find_first_of("%") != std::string::npos;
|
bool percent = item_str.find_first_of("%") != std::string::npos;
|
||||||
std::istringstream iss(item_str);
|
std::istringstream iss(item_str);
|
||||||
@ -1121,9 +1206,9 @@ protected:
|
|||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
ss << "nil";
|
ss << "nil";
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Serializing NaN");
|
throw ConfigurationError("Serializing NaN");
|
||||||
} else
|
} else
|
||||||
throw Slic3r::RuntimeError("Serializing invalid number");
|
throw ConfigurationError("Serializing invalid number");
|
||||||
}
|
}
|
||||||
static bool vectors_equal(const std::vector<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) {
|
static bool vectors_equal(const std::vector<FloatOrPercent> &v1, const std::vector<FloatOrPercent> &v2) {
|
||||||
if (NULLABLE) {
|
if (NULLABLE) {
|
||||||
@ -1312,8 +1397,15 @@ public:
|
|||||||
bool deserialize(const std::string &str, bool append = false) override
|
bool deserialize(const std::string &str, bool append = false) override
|
||||||
{
|
{
|
||||||
UNUSED(append);
|
UNUSED(append);
|
||||||
this->value = (str.compare("1") == 0);
|
if (str == "1") {
|
||||||
return true;
|
this->value = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (str == "0") {
|
||||||
|
this->value = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1375,23 +1467,38 @@ public:
|
|||||||
return vv;
|
return vv;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool deserialize(const std::string &str, bool append = false) override
|
ConfigHelpers::DeserializationResult deserialize_with_substitutions(const std::string &str, bool append, ConfigHelpers::DeserializationSubstitution substitution)
|
||||||
{
|
{
|
||||||
if (! append)
|
if (! append)
|
||||||
this->values.clear();
|
this->values.clear();
|
||||||
std::istringstream is(str);
|
std::istringstream is(str);
|
||||||
std::string item_str;
|
std::string item_str;
|
||||||
|
bool substituted = false;
|
||||||
while (std::getline(is, item_str, ',')) {
|
while (std::getline(is, item_str, ',')) {
|
||||||
boost::trim(item_str);
|
boost::trim(item_str);
|
||||||
|
unsigned char new_value = 0;
|
||||||
if (item_str == "nil") {
|
if (item_str == "nil") {
|
||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
this->values.push_back(nil_value());
|
this->values.push_back(nil_value());
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Deserializing nil into a non-nullable object");
|
throw ConfigurationError("Deserializing nil into a non-nullable object");
|
||||||
|
} else if (item_str == "1") {
|
||||||
|
new_value = true;
|
||||||
|
} else if (item_str == "0") {
|
||||||
|
new_value = false;
|
||||||
|
} else if (substitution != ConfigHelpers::DeserializationSubstitution::Disabled && ConfigHelpers::looks_like_enum_value(item_str)) {
|
||||||
|
new_value = ConfigHelpers::enum_looks_like_true_value(item_str) || substitution == ConfigHelpers::DeserializationSubstitution::DefaultsToTrue;
|
||||||
|
substituted = true;
|
||||||
} else
|
} else
|
||||||
this->values.push_back(item_str.compare("1") == 0);
|
return ConfigHelpers::DeserializationResult::Failed;
|
||||||
|
this->values.push_back(new_value);
|
||||||
}
|
}
|
||||||
return true;
|
return substituted ? ConfigHelpers::DeserializationResult::Substituted : ConfigHelpers::DeserializationResult::Loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool deserialize(const std::string &str, bool append = false) override
|
||||||
|
{
|
||||||
|
return this->deserialize_with_substitutions(str, append, ConfigHelpers::DeserializationSubstitution::Disabled) == ConfigHelpers::DeserializationResult::Loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -1400,7 +1507,7 @@ protected:
|
|||||||
if (NULLABLE)
|
if (NULLABLE)
|
||||||
ss << "nil";
|
ss << "nil";
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Serializing NaN");
|
throw ConfigurationError("Serializing NaN");
|
||||||
} else
|
} else
|
||||||
ss << (v ? "1" : "0");
|
ss << (v ? "1" : "0");
|
||||||
}
|
}
|
||||||
@ -1436,14 +1543,14 @@ public:
|
|||||||
bool operator==(const ConfigOption &rhs) const override
|
bool operator==(const ConfigOption &rhs) const override
|
||||||
{
|
{
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Comparing incompatible types");
|
throw ConfigurationError("ConfigOptionEnum<T>: Comparing incompatible types");
|
||||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||||
return this->value == (T)rhs.getInt();
|
return this->value == (T)rhs.getInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(const ConfigOption *rhs) override {
|
void set(const ConfigOption *rhs) override {
|
||||||
if (rhs->type() != this->type())
|
if (rhs->type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionEnum<T>: Assigning an incompatible type");
|
throw ConfigurationError("ConfigOptionEnum<T>: Assigning an incompatible type");
|
||||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||||
this->value = (T)rhs->getInt();
|
this->value = (T)rhs->getInt();
|
||||||
this->phony = rhs->phony;
|
this->phony = rhs->phony;
|
||||||
@ -1522,14 +1629,14 @@ public:
|
|||||||
bool operator==(const ConfigOption &rhs) const override
|
bool operator==(const ConfigOption &rhs) const override
|
||||||
{
|
{
|
||||||
if (rhs.type() != this->type())
|
if (rhs.type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Comparing incompatible types");
|
throw ConfigurationError("ConfigOptionEnumGeneric: Comparing incompatible types");
|
||||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||||
return this->value == rhs.getInt();
|
return this->value == rhs.getInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(const ConfigOption *rhs) override {
|
void set(const ConfigOption *rhs) override {
|
||||||
if (rhs->type() != this->type())
|
if (rhs->type() != this->type())
|
||||||
throw Slic3r::RuntimeError("ConfigOptionEnumGeneric: Assigning an incompatible type");
|
throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
|
||||||
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
// rhs could be of the following type: ConfigOptionEnumGeneric or ConfigOptionEnum<T>
|
||||||
this->value = rhs->getInt();
|
this->value = rhs->getInt();
|
||||||
this->phony = rhs->phony;
|
this->phony = rhs->phony;
|
||||||
@ -1585,7 +1692,7 @@ public:
|
|||||||
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
|
case coInts: { auto opt = new ConfigOptionIntsNullable(); archive(*opt); return opt; }
|
||||||
case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
|
case coPercents: { auto opt = new ConfigOptionPercentsNullable();archive(*opt); return opt; }
|
||||||
case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
|
case coBools: { auto opt = new ConfigOptionBoolsNullable(); archive(*opt); return opt; }
|
||||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (this->type) {
|
switch (this->type) {
|
||||||
@ -1604,7 +1711,7 @@ public:
|
|||||||
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
|
case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; }
|
||||||
case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
|
case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; }
|
||||||
case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
|
case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; }
|
||||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
|
default: throw ConfigurationError(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1616,7 +1723,7 @@ public:
|
|||||||
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
|
case coInts: archive(*static_cast<const ConfigOptionIntsNullable*>(opt)); break;
|
||||||
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
|
case coPercents: archive(*static_cast<const ConfigOptionPercentsNullable*>(opt));break;
|
||||||
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
|
case coBools: archive(*static_cast<const ConfigOptionBoolsNullable*>(opt)); break;
|
||||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown nullable option type for option ") + this->opt_key);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (this->type) {
|
switch (this->type) {
|
||||||
@ -1635,7 +1742,7 @@ public:
|
|||||||
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
|
case coBool: archive(*static_cast<const ConfigOptionBool*>(opt)); break;
|
||||||
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
|
case coBools: archive(*static_cast<const ConfigOptionBools*>(opt)); break;
|
||||||
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
|
case coEnum: archive(*static_cast<const ConfigOptionEnumGeneric*>(opt)); break;
|
||||||
default: throw Slic3r::RuntimeError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
|
default: throw ConfigurationError(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make the compiler happy, shut up the warnings.
|
// Make the compiler happy, shut up the warnings.
|
||||||
@ -1731,6 +1838,14 @@ public:
|
|||||||
static const constexpr char *nocli = "~~~noCLI";
|
static const constexpr char *nocli = "~~~noCLI";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool operator<(const ConfigSubstitution &lhs, const ConfigSubstitution &rhs) throw() {
|
||||||
|
return lhs.opt_def->opt_key < rhs.opt_def->opt_key ||
|
||||||
|
(lhs.opt_def->opt_key == rhs.opt_def->opt_key && lhs.old_value < rhs.old_value);
|
||||||
|
}
|
||||||
|
inline bool operator==(const ConfigSubstitution &lhs, const ConfigSubstitution &rhs) throw() {
|
||||||
|
return lhs.opt_def == rhs.opt_def && lhs.old_value == rhs.old_value;
|
||||||
|
}
|
||||||
|
|
||||||
// Map from a config option name to its definition.
|
// Map from a config option name to its definition.
|
||||||
// The definition does not carry an actual value of the config option, only its constant default value.
|
// The definition does not carry an actual value of the config option, only its constant default value.
|
||||||
// t_config_option_key is std::string
|
// t_config_option_key is std::string
|
||||||
@ -1758,7 +1873,7 @@ public:
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate through all of the CLI options and write them to a stream.
|
// Iterate through all of the CLI options and write them to a stream.
|
||||||
std::ostream& print_cli_help(
|
std::ostream& print_cli_help(
|
||||||
std::ostream& out, bool show_defaults,
|
std::ostream& out, bool show_defaults,
|
||||||
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
|
std::function<bool(const ConfigOptionDef &)> filter = [](const ConfigOptionDef &){ return true; }) const;
|
||||||
@ -1809,6 +1924,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// An abstract configuration store.
|
// An abstract configuration store.
|
||||||
class ConfigBase : public ConfigOptionResolver
|
class ConfigBase : public ConfigOptionResolver
|
||||||
{
|
{
|
||||||
@ -1905,9 +2022,11 @@ public:
|
|||||||
|
|
||||||
// Set a configuration value from a string, it will call an overridable handle_legacy()
|
// Set a configuration value from a string, it will call an overridable handle_legacy()
|
||||||
// to resolve renamed and removed configuration keys.
|
// to resolve renamed and removed configuration keys.
|
||||||
bool set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, bool append = false);
|
bool set_deserialize_nothrow(const t_config_option_key &opt_key_src, const std::string &value_src, ConfigSubstitutionContext& substitutions, bool append = false);
|
||||||
// May throw BadOptionTypeException() if the operation fails.
|
// May throw BadOptionTypeException() if the operation fails.
|
||||||
void set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false);
|
void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext& config_substitutions, bool append = false);
|
||||||
|
void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false)
|
||||||
|
{ ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; this->set_deserialize(opt_key, str, ctxt, append); }
|
||||||
struct SetDeserializeItem {
|
struct SetDeserializeItem {
|
||||||
SetDeserializeItem(const char *opt_key, const char *opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
SetDeserializeItem(const char *opt_key, const char *opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
||||||
SetDeserializeItem(const std::string &opt_key, const std::string &opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
SetDeserializeItem(const std::string &opt_key, const std::string &opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
||||||
@ -1922,17 +2041,19 @@ public:
|
|||||||
std::string opt_key; std::string opt_value; bool append = false;
|
std::string opt_key; std::string opt_value; bool append = false;
|
||||||
};
|
};
|
||||||
// May throw BadOptionTypeException() if the operation fails.
|
// May throw BadOptionTypeException() if the operation fails.
|
||||||
void set_deserialize(std::initializer_list<SetDeserializeItem> items);
|
void set_deserialize(std::initializer_list<SetDeserializeItem> items, ConfigSubstitutionContext& substitutions);
|
||||||
|
void set_deserialize_strict(std::initializer_list<SetDeserializeItem> items)
|
||||||
|
{ ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable }; this->set_deserialize(items, ctxt); }
|
||||||
|
|
||||||
double get_abs_value(const t_config_option_key &opt_key) const;
|
double get_abs_value(const t_config_option_key &opt_key) const;
|
||||||
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
|
double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const;
|
||||||
void setenv_() const;
|
void setenv_() const;
|
||||||
void load(const std::string &file);
|
ConfigSubstitutions load(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
void load_from_ini(const std::string &file);
|
ConfigSubstitutions load_from_ini(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
void load_from_gcode_file(const std::string &file);
|
ConfigSubstitutions load_from_gcode_file(const std::string &file, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
// Returns number of key/value pairs extracted.
|
// Returns number of key/value pairs extracted.
|
||||||
size_t load_from_gcode_string(const char* str);
|
size_t load_from_gcode_string(const char* str, ConfigSubstitutionContext& substitutions);
|
||||||
void load(const boost::property_tree::ptree &tree);
|
ConfigSubstitutions load(const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
void save(const std::string &file, bool to_prusa = false) const;
|
void save(const std::string &file, bool to_prusa = false) const;
|
||||||
|
|
||||||
// Set all the nullable values to nils.
|
// Set all the nullable values to nils.
|
||||||
@ -1940,7 +2061,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Set a configuration value from a string.
|
// Set a configuration value from a string.
|
||||||
bool set_deserialize_raw(const t_config_option_key &opt_key_src, const std::string &str, bool append);
|
bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Configuration store with dynamic number of configuration values.
|
// Configuration store with dynamic number of configuration values.
|
||||||
@ -2110,9 +2231,9 @@ private:
|
|||||||
template<class Archive> void serialize(Archive &ar) { ar(options); }
|
template<class Archive> void serialize(Archive &ar) { ar(options); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Configuration store with a static definition of configuration values.
|
// Configuration store with a static definition of configuration values.
|
||||||
/// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
|
// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons,
|
||||||
/// because the configuration values could be accessed directly.
|
// because the configuration values could be accessed directly.
|
||||||
class StaticConfig : public virtual ConfigBase
|
class StaticConfig : public virtual ConfigBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -427,7 +427,7 @@ namespace Slic3r {
|
|||||||
_3MF_Importer();
|
_3MF_Importer();
|
||||||
~_3MF_Importer();
|
~_3MF_Importer();
|
||||||
|
|
||||||
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, bool check_version);
|
bool load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _destroy_xml_parser();
|
void _destroy_xml_parser();
|
||||||
@ -442,16 +442,16 @@ namespace Slic3r {
|
|||||||
XML_ErrorString(XML_GetErrorCode(m_xml_parser));
|
XML_ErrorString(XML_GetErrorCode(m_xml_parser));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config);
|
bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions);
|
||||||
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||||
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||||
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions);
|
||||||
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
void _extract_sla_support_points_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||||
void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
void _extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||||
|
|
||||||
void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
void _extract_custom_gcode_per_print_z_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
|
||||||
|
|
||||||
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename);
|
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
|
||||||
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
|
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
|
||||||
|
|
||||||
// handlers to parse the .model file
|
// handlers to parse the .model file
|
||||||
@ -518,7 +518,7 @@ namespace Slic3r {
|
|||||||
bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes);
|
bool _handle_start_config_metadata(const char** attributes, unsigned int num_attributes);
|
||||||
bool _handle_end_config_metadata();
|
bool _handle_end_config_metadata();
|
||||||
|
|
||||||
bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes);
|
bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions);
|
||||||
|
|
||||||
// callbacks to parse the .model file
|
// callbacks to parse the .model file
|
||||||
static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
|
static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
|
||||||
@ -547,7 +547,7 @@ namespace Slic3r {
|
|||||||
_destroy_xml_parser();
|
_destroy_xml_parser();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, bool check_version)
|
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, bool check_version)
|
||||||
{
|
{
|
||||||
m_version = 0;
|
m_version = 0;
|
||||||
m_check_version = check_version;
|
m_check_version = check_version;
|
||||||
@ -568,7 +568,7 @@ namespace Slic3r {
|
|||||||
m_curr_characters.clear();
|
m_curr_characters.clear();
|
||||||
clear_errors();
|
clear_errors();
|
||||||
|
|
||||||
return _load_model_from_file(filename, model, config);
|
return _load_model_from_file(filename, model, config, config_substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _3MF_Importer::_destroy_xml_parser()
|
void _3MF_Importer::_destroy_xml_parser()
|
||||||
@ -590,7 +590,7 @@ namespace Slic3r {
|
|||||||
XML_StopParser(m_xml_parser, false);
|
XML_StopParser(m_xml_parser, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _3MF_Importer::_load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config)
|
bool _3MF_Importer::_load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions)
|
||||||
{
|
{
|
||||||
mz_zip_archive archive;
|
mz_zip_archive archive;
|
||||||
mz_zip_zero_struct(&archive);
|
mz_zip_zero_struct(&archive);
|
||||||
@ -653,7 +653,7 @@ namespace Slic3r {
|
|||||||
if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE))
|
if (boost::algorithm::iequals(name, LAYER_CONFIG_RANGES_FILE))
|
||||||
{
|
{
|
||||||
// extract slic3r layer config ranges file
|
// extract slic3r layer config ranges file
|
||||||
_extract_layer_config_ranges_from_archive(archive, stat);
|
_extract_layer_config_ranges_from_archive(archive, stat, config_substitutions);
|
||||||
} else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE))
|
} else if (boost::algorithm::iequals(name, SLA_SUPPORT_POINTS_FILE))
|
||||||
{
|
{
|
||||||
// extract sla support points file
|
// extract sla support points file
|
||||||
@ -665,7 +665,7 @@ namespace Slic3r {
|
|||||||
} else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE))
|
} else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE))
|
||||||
{
|
{
|
||||||
// extract slic3r print config file
|
// extract slic3r print config file
|
||||||
_extract_print_config_from_archive(archive, stat, config, filename);
|
_extract_print_config_from_archive(archive, stat, config, config_substitutions, filename);
|
||||||
print_config_parsed = true;
|
print_config_parsed = true;
|
||||||
}
|
}
|
||||||
if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_PRINT_Z_FILE))
|
if (boost::algorithm::iequals(name, CUSTOM_GCODE_PER_PRINT_Z_FILE))
|
||||||
@ -687,7 +687,8 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
//parsed superslicer/prusa files if slic3r not found
|
//parsed superslicer/prusa files if slic3r not found
|
||||||
//note that is we successfully read one of the config file, then the other ones should also have the same name
|
//note that is we successfully read one of the config file, then the other ones should also have the same name
|
||||||
auto read_from_other_storage = [this, &print_config_parsed, num_entries, &archive, &stat, &config, &model, &filename](const std::string &print_config_name, const std::string& model_config_name) -> bool {
|
auto read_from_other_storage = [this, &print_config_parsed, num_entries, &archive, &stat, &config, &model, &filename, &config_substitutions]
|
||||||
|
(const std::string &print_config_name, const std::string& model_config_name) -> bool {
|
||||||
for (mz_uint i = 0; i < num_entries; ++i)
|
for (mz_uint i = 0; i < num_entries; ++i)
|
||||||
{
|
{
|
||||||
if (mz_zip_reader_file_stat(&archive, i, &stat))
|
if (mz_zip_reader_file_stat(&archive, i, &stat))
|
||||||
@ -699,7 +700,7 @@ namespace Slic3r {
|
|||||||
if (boost::algorithm::iequals(name, print_config_name))
|
if (boost::algorithm::iequals(name, print_config_name))
|
||||||
{
|
{
|
||||||
// extract slic3r print config file
|
// extract slic3r print config file
|
||||||
_extract_print_config_from_archive(archive, stat, config, filename);
|
_extract_print_config_from_archive(archive, stat, config, config_substitutions, filename);
|
||||||
print_config_parsed = true;
|
print_config_parsed = true;
|
||||||
} else if (boost::algorithm::iequals(name, model_config_name))
|
} else if (boost::algorithm::iequals(name, model_config_name))
|
||||||
{
|
{
|
||||||
@ -774,7 +775,7 @@ namespace Slic3r {
|
|||||||
if (metadata.key == "name")
|
if (metadata.key == "name")
|
||||||
model_object->name = metadata.value;
|
model_object->name = metadata.value;
|
||||||
else
|
else
|
||||||
model_object->config.set_deserialize(metadata.key, metadata.value);
|
model_object->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// select object's detected volumes
|
// select object's detected volumes
|
||||||
@ -791,7 +792,7 @@ namespace Slic3r {
|
|||||||
volumes_ptr = &volumes;
|
volumes_ptr = &volumes;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_generate_volumes(*model_object, obj_geometry->second, *volumes_ptr))
|
if (!_generate_volumes(*model_object, obj_geometry->second, *volumes_ptr, config_substitutions))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,7 +869,12 @@ namespace Slic3r {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, const std::string& archive_filename)
|
void _3MF_Importer::_extract_print_config_from_archive(
|
||||||
|
mz_zip_archive& archive,
|
||||||
|
const mz_zip_archive_file_stat& stat,
|
||||||
|
DynamicPrintConfig& config,
|
||||||
|
ConfigSubstitutionContext& config_substitutions,
|
||||||
|
const std::string& archive_filename)
|
||||||
{
|
{
|
||||||
if (stat.m_uncomp_size > 0)
|
if (stat.m_uncomp_size > 0)
|
||||||
{
|
{
|
||||||
@ -879,7 +885,7 @@ namespace Slic3r {
|
|||||||
add_error("Error while reading config data to buffer");
|
add_error("Error while reading config data to buffer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config.load_from_gcode_string(buffer.data());
|
config.load_from_gcode_string(buffer.data(), config_substitutions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -954,7 +960,7 @@ namespace Slic3r {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _3MF_Importer::_extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
|
void _3MF_Importer::_extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions)
|
||||||
{
|
{
|
||||||
if (stat.m_uncomp_size > 0)
|
if (stat.m_uncomp_size > 0)
|
||||||
{
|
{
|
||||||
@ -1003,8 +1009,7 @@ namespace Slic3r {
|
|||||||
continue;
|
continue;
|
||||||
std::string opt_key = option.second.get<std::string>("<xmlattr>.opt_key");
|
std::string opt_key = option.second.get<std::string>("<xmlattr>.opt_key");
|
||||||
std::string value = option.second.data();
|
std::string value = option.second.data();
|
||||||
|
config.set_deserialize(opt_key, value, config_substitutions);
|
||||||
config.set_deserialize(opt_key, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config_ranges[{ min_z, max_z }].assign_config(std::move(config));
|
config_ranges[{ min_z, max_z }].assign_config(std::move(config));
|
||||||
@ -1895,7 +1900,7 @@ namespace Slic3r {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _3MF_Importer::_generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes)
|
bool _3MF_Importer::_generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions)
|
||||||
{
|
{
|
||||||
if (!object.volumes.empty())
|
if (!object.volumes.empty())
|
||||||
{
|
{
|
||||||
@ -1997,7 +2002,7 @@ namespace Slic3r {
|
|||||||
else if (metadata.key == SOURCE_IN_INCHES)
|
else if (metadata.key == SOURCE_IN_INCHES)
|
||||||
volume->source.is_converted_from_inches = metadata.value == "1";
|
volume->source.is_converted_from_inches = metadata.value == "1";
|
||||||
else
|
else
|
||||||
volume->config.set_deserialize(metadata.key, metadata.value);
|
volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2962,13 +2967,13 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version)
|
bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version)
|
||||||
{
|
{
|
||||||
if ((path == nullptr) || (config == nullptr) || (model == nullptr))
|
if (path == nullptr || model == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_3MF_Importer importer;
|
_3MF_Importer importer;
|
||||||
bool res = importer.load_model_from_file(path, *model, *config, check_version);
|
bool res = importer.load_model_from_file(path, *model, config, config_substitutions, check_version);
|
||||||
importer.log_errors();
|
importer.log_errors();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,12 @@ namespace Slic3r {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Model;
|
class Model;
|
||||||
|
struct ConfigSubstitutionContext;
|
||||||
class DynamicPrintConfig;
|
class DynamicPrintConfig;
|
||||||
struct ThumbnailData;
|
struct ThumbnailData;
|
||||||
|
|
||||||
// Load the content of a 3mf file into the given model and preset bundle.
|
// Load the content of a 3mf file into the given model and preset bundle.
|
||||||
extern bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version);
|
extern bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version);
|
||||||
|
|
||||||
// Save the given model and the config data contained in the given Print into a 3mf file.
|
// Save the given model and the config data contained in the given Print into a 3mf file.
|
||||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||||
|
@ -62,10 +62,11 @@ namespace Slic3r
|
|||||||
|
|
||||||
struct AMFParserContext
|
struct AMFParserContext
|
||||||
{
|
{
|
||||||
AMFParserContext(XML_Parser parser, DynamicPrintConfig* config, Model* model) :
|
AMFParserContext(XML_Parser parser, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model) :
|
||||||
m_parser(parser),
|
m_parser(parser),
|
||||||
m_model(*model),
|
m_model(*model),
|
||||||
m_config(config)
|
m_config(config),
|
||||||
|
m_config_substitutions(config_substitutions)
|
||||||
{
|
{
|
||||||
m_path.reserve(12);
|
m_path.reserve(12);
|
||||||
}
|
}
|
||||||
@ -256,6 +257,8 @@ struct AMFParserContext
|
|||||||
std::string m_value[5];
|
std::string m_value[5];
|
||||||
// Pointer to config to update if config data are stored inside the amf file
|
// Pointer to config to update if config data are stored inside the amf file
|
||||||
DynamicPrintConfig *m_config { nullptr };
|
DynamicPrintConfig *m_config { nullptr };
|
||||||
|
// Config substitution rules and collected config substitution log.
|
||||||
|
ConfigSubstitutionContext *m_config_substitutions { nullptr };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AMFParserContext& operator=(AMFParserContext&);
|
AMFParserContext& operator=(AMFParserContext&);
|
||||||
@ -704,8 +707,9 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case NODE_TYPE_METADATA:
|
case NODE_TYPE_METADATA:
|
||||||
if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0)
|
if ((m_config != nullptr) && strncmp(m_value[0].c_str(), SLIC3R_CONFIG_TYPE, strlen(SLIC3R_CONFIG_TYPE)) == 0) {
|
||||||
m_config->load_from_gcode_string(m_value[1].c_str());
|
m_config->load_from_gcode_string(m_value[1].c_str(), *m_config_substitutions);
|
||||||
|
}
|
||||||
else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) {
|
else if (strncmp(m_value[0].c_str(), "slic3r.", 7) == 0) {
|
||||||
const char *opt_key = m_value[0].c_str() + 7;
|
const char *opt_key = m_value[0].c_str() + 7;
|
||||||
if (print_config_def.options.find(opt_key) != print_config_def.options.end()) {
|
if (print_config_def.options.find(opt_key) != print_config_def.options.end()) {
|
||||||
@ -723,7 +727,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||||||
config = &it->second;
|
config = &it->second;
|
||||||
}
|
}
|
||||||
if (config)
|
if (config)
|
||||||
config->set_deserialize(opt_key, m_value[1]);
|
config->set_deserialize(opt_key, m_value[1], *m_config_substitutions);
|
||||||
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
|
} else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "layer_height_profile") == 0) {
|
||||||
// Parse object's layer height profile, a semicolon separated list of floats.
|
// Parse object's layer height profile, a semicolon separated list of floats.
|
||||||
char *p = m_value[1].data();
|
char *p = m_value[1].data();
|
||||||
@ -848,7 +852,7 @@ void AMFParserContext::endDocument()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load an AMF file into a provided model.
|
// Load an AMF file into a provided model.
|
||||||
bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model)
|
bool load_amf_file(const char *path, DynamicPrintConfig *config, ConfigSubstitutionContext *config_substitutions, Model *model)
|
||||||
{
|
{
|
||||||
if ((path == nullptr) || (model == nullptr))
|
if ((path == nullptr) || (model == nullptr))
|
||||||
return false;
|
return false;
|
||||||
@ -865,7 +869,7 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFParserContext ctx(parser, config, model);
|
AMFParserContext ctx(parser, config, config_substitutions, model);
|
||||||
XML_SetUserData(parser, (void*)&ctx);
|
XML_SetUserData(parser, (void*)&ctx);
|
||||||
XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement);
|
XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement);
|
||||||
XML_SetCharacterDataHandler(parser, AMFParserContext::characters);
|
XML_SetCharacterDataHandler(parser, AMFParserContext::characters);
|
||||||
@ -909,7 +913,7 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig* config, Model* model, bool check_version)
|
bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, bool check_version)
|
||||||
{
|
{
|
||||||
if (stat.m_uncomp_size == 0)
|
if (stat.m_uncomp_size == 0)
|
||||||
{
|
{
|
||||||
@ -925,7 +929,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AMFParserContext ctx(parser, config, model);
|
AMFParserContext ctx(parser, config, config_substitutions, model);
|
||||||
XML_SetUserData(parser, (void*)&ctx);
|
XML_SetUserData(parser, (void*)&ctx);
|
||||||
XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement);
|
XML_SetElementHandler(parser, AMFParserContext::startElement, AMFParserContext::endElement);
|
||||||
XML_SetCharacterDataHandler(parser, AMFParserContext::characters);
|
XML_SetCharacterDataHandler(parser, AMFParserContext::characters);
|
||||||
@ -985,7 +989,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load an AMF archive into a provided model.
|
// Load an AMF archive into a provided model.
|
||||||
bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model, bool check_version)
|
bool load_amf_archive(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, bool check_version)
|
||||||
{
|
{
|
||||||
if ((path == nullptr) || (model == nullptr))
|
if ((path == nullptr) || (model == nullptr))
|
||||||
return false;
|
return false;
|
||||||
@ -1011,7 +1015,7 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!extract_model_from_archive(archive, stat, config, model, check_version))
|
if (!extract_model_from_archive(archive, stat, config, config_substitutions, model, check_version))
|
||||||
{
|
{
|
||||||
close_zip_reader(&archive);
|
close_zip_reader(&archive);
|
||||||
printf("Archive does not contain a valid model");
|
printf("Archive does not contain a valid model");
|
||||||
@ -1053,11 +1057,11 @@ bool load_amf_archive(const char* path, DynamicPrintConfig* config, Model* model
|
|||||||
|
|
||||||
// Load an AMF file into a provided model.
|
// Load an AMF file into a provided model.
|
||||||
// If config is not a null pointer, updates it if the amf file/archive contains config data
|
// If config is not a null pointer, updates it if the amf file/archive contains config data
|
||||||
bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version)
|
bool load_amf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, bool check_version)
|
||||||
{
|
{
|
||||||
if (boost::iends_with(path, ".amf.xml"))
|
if (boost::iends_with(path, ".amf.xml"))
|
||||||
// backward compatibility with older slic3r output
|
// backward compatibility with older slic3r output
|
||||||
return load_amf_file(path, config, model);
|
return load_amf_file(path, config, config_substitutions, model);
|
||||||
else if (boost::iends_with(path, ".amf"))
|
else if (boost::iends_with(path, ".amf"))
|
||||||
{
|
{
|
||||||
boost::nowide::ifstream file(path, boost::nowide::ifstream::binary);
|
boost::nowide::ifstream file(path, boost::nowide::ifstream::binary);
|
||||||
@ -1068,7 +1072,7 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c
|
|||||||
file.read(zip_mask.data(), 2);
|
file.read(zip_mask.data(), 2);
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
return (zip_mask == "PK") ? load_amf_archive(path, config, model, check_version) : load_amf_file(path, config, model);
|
return (zip_mask == "PK") ? load_amf_archive(path, config, config_substitutions, model, check_version) : load_amf_file(path, config, config_substitutions, model);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
@ -7,7 +7,7 @@ class Model;
|
|||||||
class DynamicPrintConfig;
|
class DynamicPrintConfig;
|
||||||
|
|
||||||
// Load the content of an amf file into the given model and configuration.
|
// Load the content of an amf file into the given model and configuration.
|
||||||
extern bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool check_version);
|
extern bool load_amf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, bool check_version);
|
||||||
|
|
||||||
// Save the given model and the config data into an amf file.
|
// Save the given model and the config data into an amf file.
|
||||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||||
|
@ -286,11 +286,8 @@ static void extract_model_from_archive(
|
|||||||
volume->name = name;
|
volume->name = name;
|
||||||
}
|
}
|
||||||
// Set the extruder to the volume.
|
// Set the extruder to the volume.
|
||||||
if (extruder_id != (unsigned int)-1) {
|
if (extruder_id != (unsigned int)-1)
|
||||||
char str_extruder[64];
|
volume->config.set("extruder", int(extruder_id));
|
||||||
sprintf(str_extruder, "%ud", extruder_id);
|
|
||||||
volume->config.set_deserialize("extruder", str_extruder);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a PrusaControl project file into a provided model.
|
// Load a PrusaControl project file into a provided model.
|
||||||
|
@ -203,7 +203,7 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
|
|||||||
|
|
||||||
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
|
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
|
||||||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
|
!opt_mirror_x || !opt_mirror_y || !opt_orient)
|
||||||
throw Slic3r::FileIOError("Invalid SL1 file");
|
throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
|
||||||
|
|
||||||
RasterParams rstp;
|
RasterParams rstp;
|
||||||
|
|
||||||
@ -229,7 +229,7 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
|||||||
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
||||||
|
|
||||||
if (!opt_layerh || !opt_init_layerh)
|
if (!opt_layerh || !opt_init_layerh)
|
||||||
throw Slic3r::FileIOError("Invalid SL1 file");
|
throw Slic3r::FileIOError("Invalid SL1 / SL1S file");
|
||||||
|
|
||||||
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
||||||
}
|
}
|
||||||
@ -287,13 +287,13 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
|
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
|
||||||
{
|
{
|
||||||
ArchiveData arch = extract_sla_archive(zipfname, "png");
|
ArchiveData arch = extract_sla_archive(zipfname, "png");
|
||||||
out.load(arch.profile);
|
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void import_sla_archive(
|
ConfigSubstitutions import_sla_archive(
|
||||||
const std::string & zipfname,
|
const std::string & zipfname,
|
||||||
Vec2i32 windowsize,
|
Vec2i32 windowsize,
|
||||||
TriangleMesh & out,
|
TriangleMesh & out,
|
||||||
@ -305,7 +305,7 @@ void import_sla_archive(
|
|||||||
windowsize.y() = std::max(2, windowsize.y());
|
windowsize.y() = std::max(2, windowsize.y());
|
||||||
|
|
||||||
ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
|
ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
|
||||||
profile.load(arch.profile);
|
ConfigSubstitutions config_substitutions = profile.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||||
|
|
||||||
RasterParams rstp = get_raster_params(profile);
|
RasterParams rstp = get_raster_params(profile);
|
||||||
rstp.win = {windowsize.y(), windowsize.x()};
|
rstp.win = {windowsize.y(), windowsize.x()};
|
||||||
@ -317,6 +317,8 @@ void import_sla_archive(
|
|||||||
|
|
||||||
if (!slices.empty())
|
if (!slices.empty())
|
||||||
out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
|
out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
|
||||||
|
|
||||||
|
return config_substitutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
using ConfMap = std::map<std::string, std::string>;
|
using ConfMap = std::map<std::string, std::string>;
|
||||||
|
@ -25,23 +25,23 @@ public:
|
|||||||
void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = "") override;
|
void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = "") override;
|
||||||
};
|
};
|
||||||
|
|
||||||
void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
||||||
|
|
||||||
void import_sla_archive(
|
ConfigSubstitutions import_sla_archive(
|
||||||
const std::string & zipfname,
|
const std::string & zipfname,
|
||||||
Vec2i32 windowsize,
|
Vec2i32 windowsize,
|
||||||
TriangleMesh & out,
|
TriangleMesh & out,
|
||||||
DynamicPrintConfig & profile,
|
DynamicPrintConfig & profile,
|
||||||
std::function<bool(int)> progr = [](int) { return true; });
|
std::function<bool(int)> progr = [](int) { return true; });
|
||||||
|
|
||||||
inline void import_sla_archive(
|
inline ConfigSubstitutions import_sla_archive(
|
||||||
const std::string & zipfname,
|
const std::string & zipfname,
|
||||||
Vec2i32 windowsize,
|
Vec2i32 windowsize,
|
||||||
TriangleMesh & out,
|
TriangleMesh & out,
|
||||||
std::function<bool(int)> progr = [](int) { return true; })
|
std::function<bool(int)> progr = [](int) { return true; })
|
||||||
{
|
{
|
||||||
DynamicPrintConfig profile;
|
DynamicPrintConfig profile;
|
||||||
import_sla_archive(zipfname, windowsize, out, profile, progr);
|
return import_sla_archive(zipfname, windowsize, out, profile, progr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r::sla
|
} // namespace Slic3r::sla
|
||||||
|
@ -878,7 +878,10 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||||||
if (m_producer == EProducer::PrusaSlicer || m_producer == EProducer::SuperSlicer || m_producer == EProducer::Slic3rPE || m_producer == EProducer::Slic3r) {
|
if (m_producer == EProducer::PrusaSlicer || m_producer == EProducer::SuperSlicer || m_producer == EProducer::Slic3rPE || m_producer == EProducer::Slic3r) {
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
config.apply(FullPrintConfig::defaults());
|
config.apply(FullPrintConfig::defaults());
|
||||||
config.load_from_gcode_file(filename);
|
// Silently substitute unknown values by new ones for loading configurations from PrusaSlicer's own G-code.
|
||||||
|
// Showing substitution log or errors may make sense, but we are not really reading many values from the G-code config,
|
||||||
|
// thus a probability of incorrect substitution is low and the G-code viewer is a consumer-only anyways.
|
||||||
|
config.load_from_gcode_file(filename, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||||
apply_config(config);
|
apply_config(config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ public:
|
|||||||
|
|
||||||
// collection of expolygons representing the bridged areas (thus not
|
// collection of expolygons representing the bridged areas (thus not
|
||||||
// needing support material)
|
// needing support material)
|
||||||
Polygons bridged;
|
// Polygons bridged;
|
||||||
|
|
||||||
// collection of polylines representing the unsupported bridge edges
|
// collection of polylines representing the unsupported bridge edges
|
||||||
Polylines unsupported_bridge_edges;
|
Polylines unsupported_bridge_edges;
|
||||||
|
@ -327,7 +327,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||||||
}else if (bd.detect_angle(custom_angle)) {
|
}else if (bd.detect_angle(custom_angle)) {
|
||||||
bridges[idx_last].bridge_angle = bd.angle;
|
bridges[idx_last].bridge_angle = bd.angle;
|
||||||
if (this->layer()->object()->config().support_material) {
|
if (this->layer()->object()->config().support_material) {
|
||||||
polygons_append(this->bridged, intersection(bd.coverage(), to_polygons(initial)));
|
//polygons_append(this->bridged, intersection(bd.coverage(), to_polygons(initial)));
|
||||||
append(this->unsupported_bridge_edges, bd.unsupported_edges());
|
append(this->unsupported_bridge_edges, bd.unsupported_edges());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -121,13 +121,17 @@ bool Model::equals(const Model& rhs) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, bool add_default_instances, bool check_version)
|
// Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
|
||||||
|
Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, LoadAttributes options)
|
||||||
{
|
{
|
||||||
Model model;
|
Model model;
|
||||||
|
|
||||||
DynamicPrintConfig temp_config;
|
DynamicPrintConfig temp_config;
|
||||||
|
ConfigSubstitutionContext temp_config_substitutions_context(ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||||
if (config == nullptr)
|
if (config == nullptr)
|
||||||
config = &temp_config;
|
config = &temp_config;
|
||||||
|
if (config_substitutions == nullptr)
|
||||||
|
config_substitutions = &temp_config_substitutions_context;
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (boost::algorithm::iends_with(input_file, ".stl"))
|
if (boost::algorithm::iends_with(input_file, ".stl"))
|
||||||
@ -135,9 +139,10 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||||||
else if (boost::algorithm::iends_with(input_file, ".obj"))
|
else if (boost::algorithm::iends_with(input_file, ".obj"))
|
||||||
result = load_obj(input_file.c_str(), &model);
|
result = load_obj(input_file.c_str(), &model);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
|
else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
|
||||||
result = load_amf(input_file.c_str(), config, &model, check_version);
|
result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||||
result = load_3mf(input_file.c_str(), config, &model, false);
|
//FIXME options & LoadAttribute::CheckVersion ?
|
||||||
|
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".prusa"))
|
else if (boost::algorithm::iends_with(input_file, ".prusa"))
|
||||||
result = load_prus(input_file.c_str(), &model);
|
result = load_prus(input_file.c_str(), &model);
|
||||||
else
|
else
|
||||||
@ -152,24 +157,29 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||||||
for (ModelObject *o : model.objects)
|
for (ModelObject *o : model.objects)
|
||||||
o->input_file = input_file;
|
o->input_file = input_file;
|
||||||
|
|
||||||
if (add_default_instances)
|
if (options & LoadAttribute::AddDefaultInstances)
|
||||||
model.add_default_instances();
|
model.add_default_instances();
|
||||||
|
|
||||||
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
|
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
|
||||||
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
|
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
|
||||||
|
|
||||||
|
sort_remove_duplicates(config_substitutions->substitutions);
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, bool add_default_instances, bool check_version)
|
// Loading model from a file (3MF or AMF), not from a simple geometry file (STL or OBJ).
|
||||||
|
Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, LoadAttributes options)
|
||||||
{
|
{
|
||||||
|
assert(config != nullptr);
|
||||||
|
assert(config_substitutions != nullptr);
|
||||||
|
|
||||||
Model model;
|
Model model;
|
||||||
|
|
||||||
bool result = false;
|
bool result = false;
|
||||||
if (boost::algorithm::iends_with(input_file, ".3mf"))
|
if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||||
result = load_3mf(input_file.c_str(), config, &model, check_version);
|
result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
|
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
|
||||||
result = load_amf(input_file.c_str(), config, &model, check_version);
|
result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
|
||||||
else
|
else
|
||||||
throw Slic3r::RuntimeError("Unknown file format. Input file must have .3mf or .zip.amf extension.");
|
throw Slic3r::RuntimeError("Unknown file format. Input file must have .3mf or .zip.amf extension.");
|
||||||
|
|
||||||
@ -191,7 +201,7 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
|||||||
o->input_file = input_file;
|
o->input_file = input_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (add_default_instances)
|
if (options & LoadAttribute::AddDefaultInstances)
|
||||||
model.add_default_instances();
|
model.add_default_instances();
|
||||||
|
|
||||||
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
|
CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
|
||||||
@ -424,13 +434,12 @@ bool Model::looks_like_multipart_object() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate next extruder ID string, in the range of (1, max_extruders).
|
// Generate next extruder ID string, in the range of (1, max_extruders).
|
||||||
static inline std::string auto_extruder_id(unsigned int max_extruders, unsigned int &cntr)
|
static inline int auto_extruder_id(unsigned int max_extruders, unsigned int &cntr)
|
||||||
{
|
{
|
||||||
char str_extruder[64];
|
int out = ++ cntr;
|
||||||
sprintf(str_extruder, "%ud", cntr + 1);
|
if (cntr == max_extruders)
|
||||||
if (++ cntr == max_extruders)
|
|
||||||
cntr = 0;
|
cntr = 0;
|
||||||
return str_extruder;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::convert_multipart_object(unsigned int max_extruders)
|
void Model::convert_multipart_object(unsigned int max_extruders)
|
||||||
@ -457,7 +466,7 @@ void Model::convert_multipart_object(unsigned int max_extruders)
|
|||||||
auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
|
auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
|
||||||
assert(new_v != nullptr);
|
assert(new_v != nullptr);
|
||||||
new_v->name = o->name + "_" + std::to_string(counter++);
|
new_v->name = o->name + "_" + std::to_string(counter++);
|
||||||
new_v->config.set_deserialize("extruder", auto_extruder_id(max_extruders, extruder_counter));
|
new_v->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
|
||||||
return new_v;
|
return new_v;
|
||||||
};
|
};
|
||||||
if (o->instances.empty()) {
|
if (o->instances.empty()) {
|
||||||
@ -1766,7 +1775,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||||||
this->object->volumes[ivolume]->center_geometry_after_creation();
|
this->object->volumes[ivolume]->center_geometry_after_creation();
|
||||||
this->object->volumes[ivolume]->translate(offset);
|
this->object->volumes[ivolume]->translate(offset);
|
||||||
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
|
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
|
||||||
this->object->volumes[ivolume]->config.set_deserialize("extruder", auto_extruder_id(max_extruders, extruder_counter));
|
this->object->volumes[ivolume]->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
|
||||||
delete mesh;
|
delete mesh;
|
||||||
++ idx;
|
++ idx;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
#include "Arrange.hpp"
|
#include "Arrange.hpp"
|
||||||
#include "CustomGCode.hpp"
|
#include "CustomGCode.hpp"
|
||||||
|
#include "enum_bitmask.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -996,8 +997,20 @@ public:
|
|||||||
|
|
||||||
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model)
|
OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model)
|
||||||
|
|
||||||
static Model read_from_file(const std::string& input_file, DynamicPrintConfig* config = nullptr, bool add_default_instances = true, bool check_version = false);
|
enum class LoadAttribute : int {
|
||||||
static Model read_from_archive(const std::string& input_file, DynamicPrintConfig* config, bool add_default_instances = true, bool check_version = false);
|
AddDefaultInstances,
|
||||||
|
CheckVersion
|
||||||
|
};
|
||||||
|
using LoadAttributes = enum_bitmask<LoadAttribute>;
|
||||||
|
|
||||||
|
static Model read_from_file(
|
||||||
|
const std::string& input_file,
|
||||||
|
DynamicPrintConfig* config = nullptr, ConfigSubstitutionContext* config_substitutions = nullptr,
|
||||||
|
LoadAttributes options = LoadAttribute::AddDefaultInstances);
|
||||||
|
static Model read_from_archive(
|
||||||
|
const std::string& input_file,
|
||||||
|
DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions,
|
||||||
|
LoadAttributes options = LoadAttribute::AddDefaultInstances);
|
||||||
|
|
||||||
bool equals(const Model& rhs) const;
|
bool equals(const Model& rhs) const;
|
||||||
|
|
||||||
@ -1062,6 +1075,8 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ENABLE_ENUM_BITMASK_OPERATORS(Model::LoadAttribute)
|
||||||
|
|
||||||
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
|
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
|
||||||
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
|
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
|
||||||
|
|
||||||
|
@ -919,7 +919,9 @@ void PresetCollection::add_default_preset(const std::vector<std::string> &keys,
|
|||||||
|
|
||||||
// Load all presets found in dir_path.
|
// Load all presets found in dir_path.
|
||||||
// Throws an exception on error.
|
// Throws an exception on error.
|
||||||
void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir)
|
void PresetCollection::load_presets(
|
||||||
|
const std::string &dir_path, const std::string &subdir,
|
||||||
|
PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule)
|
||||||
{
|
{
|
||||||
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
||||||
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
||||||
@ -946,7 +948,9 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri
|
|||||||
// Load the preset file, apply preset values on top of defaults.
|
// Load the preset file, apply preset values on top of defaults.
|
||||||
try {
|
try {
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
config.load_from_ini(preset.file);
|
ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
|
||||||
|
if (! config_substitutions.empty())
|
||||||
|
substitutions.push_back({ preset.name, m_type, PresetConfigSubstitutions::Source::UserFile, preset.file, std::move(config_substitutions) });
|
||||||
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
|
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
|
||||||
const Preset &default_preset = this->default_preset_for(config);
|
const Preset &default_preset = this->default_preset_for(config);
|
||||||
preset.config = default_preset.config;
|
preset.config = default_preset.config;
|
||||||
@ -1829,7 +1833,9 @@ PhysicalPrinterCollection::PhysicalPrinterCollection( const std::vector<std::str
|
|||||||
|
|
||||||
// Load all printers found in dir_path.
|
// Load all printers found in dir_path.
|
||||||
// Throws an exception on error.
|
// Throws an exception on error.
|
||||||
void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const std::string& subdir)
|
void PhysicalPrinterCollection::load_printers(
|
||||||
|
const std::string& dir_path, const std::string& subdir,
|
||||||
|
PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule substitution_rule)
|
||||||
{
|
{
|
||||||
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
// Don't use boost::filesystem::canonical() on Windows, it is broken in regard to reparse points,
|
||||||
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
// see https://github.com/prusa3d/PrusaSlicer/issues/732
|
||||||
@ -1855,7 +1861,9 @@ void PhysicalPrinterCollection::load_printers(const std::string& dir_path, const
|
|||||||
// Load the preset file, apply preset values on top of defaults.
|
// Load the preset file, apply preset values on top of defaults.
|
||||||
try {
|
try {
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
config.load_from_ini(printer.file);
|
ConfigSubstitutions config_substitutions = config.load_from_ini(printer.file, substitution_rule);
|
||||||
|
if (! config_substitutions.empty())
|
||||||
|
substitutions.push_back({ name, Preset::Type::TYPE_PHYSICAL_PRINTER, PresetConfigSubstitutions::Source::UserFile, printer.file, std::move(config_substitutions) });
|
||||||
printer.update_from_config(config);
|
printer.update_from_config(config);
|
||||||
printer.loaded = true;
|
printer.loaded = true;
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,10 @@ public:
|
|||||||
TYPE_SLA_PRINT = TYPE_SLA | TYPE_PRINT1,
|
TYPE_SLA_PRINT = TYPE_SLA | TYPE_PRINT1,
|
||||||
TYPE_SLA_MATERIAL = TYPE_SLA | TYPE_MATERIAL,
|
TYPE_SLA_MATERIAL = TYPE_SLA | TYPE_MATERIAL,
|
||||||
TYPE_TECHNOLOGY = TYPE_FFF | TYPE_SLA,
|
TYPE_TECHNOLOGY = TYPE_FFF | TYPE_SLA,
|
||||||
|
|
||||||
|
// This type is here to support PresetConfigSubstitutions for physical printers, however it does not belong to the Preset class,
|
||||||
|
// PhysicalPrinter class is used instead.
|
||||||
|
TYPE_PHYSICAL_PRINTER = 1 << 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
|
Preset(Type type, const std::string &name, bool is_default = false) : type(type), is_default(is_default), name(name) {}
|
||||||
@ -278,6 +282,27 @@ enum class PresetSelectCompatibleType {
|
|||||||
Always
|
Always
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Substitutions having been performed during parsing a single configuration file.
|
||||||
|
struct PresetConfigSubstitutions {
|
||||||
|
// User readable preset name.
|
||||||
|
std::string preset_name;
|
||||||
|
// Type of the preset (Print / Filament / Printer ...)
|
||||||
|
Preset::Type preset_type;
|
||||||
|
enum class Source {
|
||||||
|
UserFile,
|
||||||
|
ConfigBundle,
|
||||||
|
};
|
||||||
|
Source preset_source;
|
||||||
|
// Source of the preset. It may be empty in case of a ConfigBundle being loaded.
|
||||||
|
std::string preset_file;
|
||||||
|
// What config value has been substituted with what.
|
||||||
|
ConfigSubstitutions substitutions;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Substitutions having been performed during parsing a set of configuration files, for example when starting up
|
||||||
|
// PrusaSlicer and reading the user Print / Filament / Printer profiles.
|
||||||
|
using PresetsConfigSubstitutions = std::vector<PresetConfigSubstitutions>;
|
||||||
|
|
||||||
// Collections of presets of the same type (one of the Print, Filament or Printer type).
|
// Collections of presets of the same type (one of the Print, Filament or Printer type).
|
||||||
class PresetCollection
|
class PresetCollection
|
||||||
{
|
{
|
||||||
@ -308,7 +333,7 @@ public:
|
|||||||
void add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name);
|
void add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name);
|
||||||
|
|
||||||
// Load ini files of the particular type from the provided directory path.
|
// Load ini files of the particular type from the provided directory path.
|
||||||
void load_presets(const std::string &dir_path, const std::string &subdir);
|
void load_presets(const std::string &dir_path, const std::string &subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule);
|
||||||
|
|
||||||
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
|
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
|
||||||
// and select it, losing previous modifications.
|
// and select it, losing previous modifications.
|
||||||
@ -680,7 +705,7 @@ public:
|
|||||||
const std::deque<PhysicalPrinter>& operator()() const { return m_printers; }
|
const std::deque<PhysicalPrinter>& operator()() const { return m_printers; }
|
||||||
|
|
||||||
// Load ini files of the particular type from the provided directory path.
|
// Load ini files of the particular type from the provided directory path.
|
||||||
void load_printers(const std::string& dir_path, const std::string& subdir);
|
void load_printers(const std::string& dir_path, const std::string& subdir, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule);
|
||||||
void load_printers_from_presets(PrinterPresetCollection &printer_presets);
|
void load_printers_from_presets(PrinterPresetCollection &printer_presets);
|
||||||
// Load printer from the loaded configuration
|
// Load printer from the loaded configuration
|
||||||
void load_printer(const std::string& path, const std::string& name, DynamicPrintConfig&& config, bool select, bool save=false);
|
void load_printer(const std::string& path, const std::string& name, DynamicPrintConfig&& config, bool select, bool save=false);
|
||||||
|
@ -162,10 +162,12 @@ void PresetBundle::setup_directories()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_model_id)
|
PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule substitution_rule, const std::string &preferred_model_id)
|
||||||
{
|
{
|
||||||
// First load the vendor specific system presets.
|
// First load the vendor specific system presets.
|
||||||
std::string errors_cummulative = this->load_system_presets();
|
PresetsConfigSubstitutions substitutions;
|
||||||
|
std::string errors_cummulative;
|
||||||
|
std::tie(substitutions, errors_cummulative) = this->load_system_presets(substitution_rule);
|
||||||
|
|
||||||
const std::string dir_user_presets = data_dir()
|
const std::string dir_user_presets = data_dir()
|
||||||
#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR
|
#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR
|
||||||
@ -175,33 +177,34 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_
|
|||||||
// Store the print/filament/printer presets at the same location as the upstream Slic3r.
|
// Store the print/filament/printer presets at the same location as the upstream Slic3r.
|
||||||
#endif
|
#endif
|
||||||
;
|
;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this->prints.load_presets(dir_user_presets, "print");
|
this->prints.load_presets(dir_user_presets, "print", substitutions, substitution_rule);
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
errors_cummulative += err.what();
|
errors_cummulative += err.what();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this->sla_prints.load_presets(dir_user_presets, "sla_print");
|
this->sla_prints.load_presets(dir_user_presets, "sla_print", substitutions, substitution_rule);
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
errors_cummulative += err.what();
|
errors_cummulative += err.what();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this->filaments.load_presets(dir_user_presets, "filament");
|
this->filaments.load_presets(dir_user_presets, "filament", substitutions, substitution_rule);
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
errors_cummulative += err.what();
|
errors_cummulative += err.what();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this->sla_materials.load_presets(dir_user_presets, "sla_material");
|
this->sla_materials.load_presets(dir_user_presets, "sla_material", substitutions, substitution_rule);
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
errors_cummulative += err.what();
|
errors_cummulative += err.what();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this->printers.load_presets(dir_user_presets, "printer");
|
this->printers.load_presets(dir_user_presets, "printer", substitutions, substitution_rule);
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
errors_cummulative += err.what();
|
errors_cummulative += err.what();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this->physical_printers.load_printers(dir_user_presets, "physical_printer");
|
this->physical_printers.load_printers(dir_user_presets, "physical_printer", substitutions, substitution_rule);
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
errors_cummulative += err.what();
|
errors_cummulative += err.what();
|
||||||
}
|
}
|
||||||
@ -211,16 +214,26 @@ void PresetBundle::load_presets(AppConfig &config, const std::string &preferred_
|
|||||||
throw Slic3r::RuntimeError(errors_cummulative);
|
throw Slic3r::RuntimeError(errors_cummulative);
|
||||||
|
|
||||||
this->load_selections(config, preferred_model_id);
|
this->load_selections(config, preferred_model_id);
|
||||||
|
|
||||||
|
return substitutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load system presets into this PresetBundle.
|
// Load system presets into this PresetBundle.
|
||||||
// For each vendor, there will be a single PresetBundle loaded.
|
// For each vendor, there will be a single PresetBundle loaded.
|
||||||
std::string PresetBundle::load_system_presets()
|
std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_presets(ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
|
if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)
|
||||||
|
// Loading system presets, don't log substitutions.
|
||||||
|
compatibility_rule = ForwardCompatibilitySubstitutionRule::EnableSilent;
|
||||||
|
else if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem)
|
||||||
|
// Loading system presets, throw on unknown option value.
|
||||||
|
compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable;
|
||||||
|
|
||||||
// Here the vendor specific read only Config Bundles are stored.
|
// Here the vendor specific read only Config Bundles are stored.
|
||||||
boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / "vendor").make_preferred();
|
boost::filesystem::path dir = (boost::filesystem::path(data_dir()) / "vendor").make_preferred();
|
||||||
std::string errors_cummulative;
|
PresetsConfigSubstitutions substitutions;
|
||||||
bool first = true;
|
std::string errors_cummulative;
|
||||||
|
bool first = true;
|
||||||
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
|
for (auto &dir_entry : boost::filesystem::directory_iterator(dir))
|
||||||
if (Slic3r::is_ini_file(dir_entry)) {
|
if (Slic3r::is_ini_file(dir_entry)) {
|
||||||
std::string name = dir_entry.path().filename().string();
|
std::string name = dir_entry.path().filename().string();
|
||||||
@ -230,13 +243,13 @@ std::string PresetBundle::load_system_presets()
|
|||||||
// Load the config bundle, flatten it.
|
// Load the config bundle, flatten it.
|
||||||
if (first) {
|
if (first) {
|
||||||
// Reset this PresetBundle and load the first vendor config.
|
// Reset this PresetBundle and load the first vendor config.
|
||||||
this->load_configbundle(dir_entry.path().string(), LOAD_CFGBNDLE_SYSTEM);
|
append(substitutions, this->load_configbundle(dir_entry.path().string(), PresetBundle::LoadSystem, compatibility_rule).first);
|
||||||
first = false;
|
first = false;
|
||||||
} else {
|
} else {
|
||||||
// Load the other vendor configs, merge them with this PresetBundle.
|
// Load the other vendor configs, merge them with this PresetBundle.
|
||||||
// Report duplicate profiles.
|
// Report duplicate profiles.
|
||||||
PresetBundle other;
|
PresetBundle other;
|
||||||
other.load_configbundle(dir_entry.path().string(), LOAD_CFGBNDLE_SYSTEM);
|
append(substitutions, other.load_configbundle(dir_entry.path().string(), PresetBundle::LoadSystem, compatibility_rule).first);
|
||||||
std::vector<std::string> duplicates = this->merge_presets(std::move(other));
|
std::vector<std::string> duplicates = this->merge_presets(std::move(other));
|
||||||
if (! duplicates.empty()) {
|
if (! duplicates.empty()) {
|
||||||
errors_cummulative += "Vendor configuration file " + name + " contains the following presets with names used by other vendors: ";
|
errors_cummulative += "Vendor configuration file " + name + " contains the following presets with names used by other vendors: ";
|
||||||
@ -258,7 +271,7 @@ std::string PresetBundle::load_system_presets()
|
|||||||
}
|
}
|
||||||
|
|
||||||
this->update_system_maps();
|
this->update_system_maps();
|
||||||
return errors_cummulative;
|
return std::make_pair(std::move(substitutions), errors_cummulative);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||||
@ -666,15 +679,15 @@ DynamicPrintConfig PresetBundle::full_sla_config() const
|
|||||||
// Instead of a config file, a G-code may be loaded containing the full set of parameters.
|
// Instead of a config file, a G-code may be loaded containing the full set of parameters.
|
||||||
// In the future the configuration will likely be read from an AMF file as well.
|
// In the future the configuration will likely be read from an AMF file as well.
|
||||||
// If the file is loaded successfully, its print / filament / printer profiles will be activated.
|
// If the file is loaded successfully, its print / filament / printer profiles will be activated.
|
||||||
void PresetBundle::load_config_file(const std::string &path)
|
ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
if (is_gcode_file(path)) {
|
if (is_gcode_file(path)) {
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
config.apply(FullPrintConfig::defaults());
|
config.apply(FullPrintConfig::defaults());
|
||||||
config.load_from_gcode_file(path);
|
ConfigSubstitutions config_substitutions = config.load_from_gcode_file(path, compatibility_rule);
|
||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
load_config_file_config(path, true, std::move(config));
|
load_config_file_config(path, true, std::move(config));
|
||||||
return;
|
return config_substitutions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1) Try to load the config file into a boost property tree.
|
// 1) Try to load the config file into a boost property tree.
|
||||||
@ -685,33 +698,41 @@ void PresetBundle::load_config_file(const std::string &path)
|
|||||||
} catch (const std::ifstream::failure &err) {
|
} catch (const std::ifstream::failure &err) {
|
||||||
throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what());
|
throw Slic3r::RuntimeError(std::string("The Config Bundle cannot be loaded: ") + path + "\n\tReason: " + err.what());
|
||||||
} catch (const boost::property_tree::file_parser_error &err) {
|
} catch (const boost::property_tree::file_parser_error &err) {
|
||||||
throw Slic3r::RuntimeError((boost::format("Failed loading the Config Bundle \"%1%\": %2% at line %3%")
|
throw Slic3r::RuntimeError(format("Failed loading the Config Bundle \"%1%\": %2% at line %3%",
|
||||||
% err.filename() % err.message() % err.line()).str());
|
err.filename(), err.message(), err.line()));
|
||||||
} catch (const std::runtime_error &err) {
|
} catch (const std::runtime_error &err) {
|
||||||
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
|
throw Slic3r::RuntimeError(std::string("Failed loading the preset file: ") + path + "\n\tReason: " + err.what());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Continue based on the type of the configuration file.
|
// 2) Continue based on the type of the configuration file.
|
||||||
ConfigFileType config_file_type = guess_config_file_type(tree);
|
ConfigFileType config_file_type = guess_config_file_type(tree);
|
||||||
switch (config_file_type) {
|
ConfigSubstitutions config_substitutions;
|
||||||
case CONFIG_FILE_TYPE_UNKNOWN:
|
try {
|
||||||
throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path);
|
switch (config_file_type) {
|
||||||
case CONFIG_FILE_TYPE_APP_CONFIG:
|
case CONFIG_FILE_TYPE_UNKNOWN:
|
||||||
throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file.");
|
throw Slic3r::RuntimeError(std::string("Unknown configuration file type: ") + path);
|
||||||
case CONFIG_FILE_TYPE_CONFIG:
|
case CONFIG_FILE_TYPE_APP_CONFIG:
|
||||||
{
|
throw Slic3r::RuntimeError(std::string("Invalid configuration file: ") + path + ". This is an application config file.");
|
||||||
// Initialize a config from full defaults.
|
case CONFIG_FILE_TYPE_CONFIG:
|
||||||
DynamicPrintConfig config;
|
{
|
||||||
config.apply(FullPrintConfig::defaults());
|
// Initialize a config from full defaults.
|
||||||
config.load(tree);
|
DynamicPrintConfig config;
|
||||||
Preset::normalize(config);
|
config.apply(FullPrintConfig::defaults());
|
||||||
load_config_file_config(path, true, std::move(config));
|
config_substitutions = config.load(tree, compatibility_rule);
|
||||||
break;
|
Preset::normalize(config);
|
||||||
}
|
load_config_file_config(path, true, std::move(config));
|
||||||
case CONFIG_FILE_TYPE_CONFIG_BUNDLE:
|
return config_substitutions;
|
||||||
load_config_file_config_bundle(path, tree);
|
}
|
||||||
break;
|
case CONFIG_FILE_TYPE_CONFIG_BUNDLE:
|
||||||
|
return load_config_file_config_bundle(path, tree, compatibility_rule);
|
||||||
|
}
|
||||||
|
} catch (const ConfigurationError &e) {
|
||||||
|
throw Slic3r::RuntimeError(format("Invalid configuration file %1%: %2%", path, e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This shall never happen. Suppres compiler warnings.
|
||||||
|
assert(false);
|
||||||
|
return ConfigSubstitutions{};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a config file from a boost property_tree. This is a private method called from load_config_file.
|
// Load a config file from a boost property_tree. This is a private method called from load_config_file.
|
||||||
@ -883,16 +904,24 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file.
|
// Load the active configuration of a config bundle from a boost property_tree. This is a private method called from load_config_file.
|
||||||
void PresetBundle::load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree)
|
ConfigSubstitutions PresetBundle::load_config_file_config_bundle(
|
||||||
|
const std::string &path, const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
// 1) Load the config bundle into a temp data.
|
// 1) Load the config bundle into a temp data.
|
||||||
PresetBundle tmp_bundle;
|
PresetBundle tmp_bundle;
|
||||||
// Load the config bundle, don't save the loaded presets to user profile directory.
|
// Load the config bundle, but don't save the loaded presets to user profile directory, as only the presets marked as active in the loaded preset bundle
|
||||||
tmp_bundle.load_configbundle(path, 0);
|
// will be loaded into the master PresetBundle and activated.
|
||||||
|
auto [presets_substitutions, presets_imported] = tmp_bundle.load_configbundle(path, {}, compatibility_rule);
|
||||||
|
|
||||||
std::string bundle_name = std::string(" - ") + boost::filesystem::path(path).filename().string();
|
std::string bundle_name = std::string(" - ") + boost::filesystem::path(path).filename().string();
|
||||||
|
|
||||||
// 2) Extract active configs from the config bundle, copy them and activate them in this bundle.
|
// 2) Extract active configs from the config bundle, copy them and activate them in this bundle.
|
||||||
auto load_one = [this, &path, &bundle_name](PresetCollection &collection_dst, PresetCollection &collection_src, const std::string &preset_name_src, bool activate) -> std::string {
|
ConfigSubstitutions config_substitutions;
|
||||||
|
auto load_one = [this, &path, &bundle_name, &presets_substitutions = presets_substitutions, &config_substitutions](PresetCollection &collection_dst, PresetCollection &collection_src, const std::string &preset_name_src, bool activate) -> std::string {
|
||||||
|
// If there are substitutions reported for this preset, move them to config_substitutions.
|
||||||
|
if (auto it = std::find_if(presets_substitutions.begin(), presets_substitutions.end(), [&preset_name_src](const PresetConfigSubstitutions& subs){ return subs.preset_name == preset_name_src; });
|
||||||
|
it != presets_substitutions.end() && ! it->substitutions.empty())
|
||||||
|
append(config_substitutions, std::move(it->substitutions));
|
||||||
Preset *preset_src = collection_src.find_preset(preset_name_src, false);
|
Preset *preset_src = collection_src.find_preset(preset_name_src, false);
|
||||||
Preset *preset_dst = collection_dst.find_preset(preset_name_src, false);
|
Preset *preset_dst = collection_dst.find_preset(preset_name_src, false);
|
||||||
assert(preset_src != nullptr);
|
assert(preset_src != nullptr);
|
||||||
@ -942,6 +971,9 @@ void PresetBundle::load_config_file_config_bundle(const std::string &path, const
|
|||||||
this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false);
|
this->filament_presets[i] = load_one(this->filaments, tmp_bundle.filaments, tmp_bundle.filament_presets[i], false);
|
||||||
|
|
||||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||||
|
|
||||||
|
sort_remove_duplicates(config_substitutions);
|
||||||
|
return std::move(config_substitutions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the Config Bundle loaded as a Boost property tree.
|
// Process the Config Bundle loaded as a Boost property tree.
|
||||||
@ -1086,11 +1118,16 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree, co
|
|||||||
|
|
||||||
// Load a config bundle file, into presets and store the loaded presets into separate files
|
// Load a config bundle file, into presets and store the loaded presets into separate files
|
||||||
// of the local configuration directory.
|
// of the local configuration directory.
|
||||||
size_t PresetBundle::load_configbundle(const std::string &path, unsigned int flags)
|
std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_configbundle(
|
||||||
|
const std::string &path, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule)
|
||||||
{
|
{
|
||||||
if (flags & (LOAD_CFGBNDLE_RESET_USER_PROFILE | LOAD_CFGBNDLE_SYSTEM))
|
// Enable substitutions for user config bundle, throw an exception when loading a system profile.
|
||||||
// Reset this bundle, delete user profile files if LOAD_CFGBNDLE_SAVE.
|
ConfigSubstitutionContext substitution_context { compatibility_rule };
|
||||||
this->reset(flags & LOAD_CFGBNDLE_SAVE);
|
PresetsConfigSubstitutions substitutions;
|
||||||
|
|
||||||
|
if (flags.has(LoadConfigBundleAttribute::ResetUserProfile) || flags.has(LoadConfigBundleAttribute::LoadSystem))
|
||||||
|
// Reset this bundle, delete user profile files if SaveImported.
|
||||||
|
this->reset(flags.has(LoadConfigBundleAttribute::SaveImported));
|
||||||
|
|
||||||
// 1) Read the complete config file into a boost::property_tree.
|
// 1) Read the complete config file into a boost::property_tree.
|
||||||
namespace pt = boost::property_tree;
|
namespace pt = boost::property_tree;
|
||||||
@ -1103,25 +1140,24 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
}
|
}
|
||||||
|
|
||||||
const VendorProfile *vendor_profile = nullptr;
|
const VendorProfile *vendor_profile = nullptr;
|
||||||
if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) {
|
if (flags.has(LoadConfigBundleAttribute::LoadSystem) || flags.has(LoadConfigBundleAttribute::LoadVendorOnly)) {
|
||||||
auto vp = VendorProfile::from_ini(tree, path);
|
auto vp = VendorProfile::from_ini(tree, path);
|
||||||
if (vp.models.size() == 0) {
|
if (vp.models.size() == 0) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No printer model defined.") % path;
|
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No printer model defined.") % path;
|
||||||
return 0;
|
return std::make_pair(PresetsConfigSubstitutions{}, 0);
|
||||||
} else if (vp.num_variants() == 0) {
|
} else if (vp.num_variants() == 0) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No printer variant defined") % path;
|
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No printer variant defined") % path;
|
||||||
return 0;
|
return std::make_pair(PresetsConfigSubstitutions{}, 0);
|
||||||
}
|
}
|
||||||
vendor_profile = &this->vendors.insert({vp.id, vp}).first->second;
|
vendor_profile = &this->vendors.insert({vp.id, vp}).first->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & LOAD_CFGBUNDLE_VENDOR_ONLY) {
|
if (flags.has(LoadConfigBundleAttribute::LoadVendorOnly))
|
||||||
return 0;
|
return std::make_pair(PresetsConfigSubstitutions{}, 0);
|
||||||
}
|
|
||||||
|
|
||||||
// 1.5) Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed.
|
// 1.5) Flatten the config bundle by applying the inheritance rules. Internal profiles (with names starting with '*') are removed.
|
||||||
// If loading a user config bundle, do not flatten with the system profiles, but keep the "inherits" flag intact.
|
// If loading a user config bundle, do not flatten with the system profiles, but keep the "inherits" flag intact.
|
||||||
flatten_configbundle_hierarchy(tree, ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) ? this : nullptr);
|
flatten_configbundle_hierarchy(tree, flags.has(LoadConfigBundleAttribute::LoadSystem) ? nullptr : this);
|
||||||
|
|
||||||
// 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
|
// 2) Parse the property_tree, extract the active preset names and the profiles, save them into local config files.
|
||||||
// Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.
|
// Parse the obsolete preset names, to be deleted when upgrading from the old configuration structure.
|
||||||
@ -1188,7 +1224,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
active_sla_material = kvp.second.data();
|
active_sla_material = kvp.second.data();
|
||||||
} else if (kvp.first == "printer") {
|
} else if (kvp.first == "printer") {
|
||||||
active_printer = kvp.second.data();
|
active_printer = kvp.second.data();
|
||||||
}else if (kvp.first == "physical_printer") {
|
} else if (kvp.first == "physical_printer") {
|
||||||
active_physical_printer = kvp.second.data();
|
active_physical_printer = kvp.second.data();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1225,30 +1261,36 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
std::string alias_name;
|
std::string alias_name;
|
||||||
std::vector<std::string> renamed_from;
|
std::vector<std::string> renamed_from;
|
||||||
auto parse_config_section = [§ion, &alias_name, &renamed_from, &path](DynamicPrintConfig &config) {
|
try {
|
||||||
for (auto &kvp : section.second) {
|
auto parse_config_section = [§ion, &alias_name, &renamed_from, &substitution_context, &path](DynamicPrintConfig &config) {
|
||||||
if (kvp.first == "alias")
|
substitution_context.substitutions.clear();
|
||||||
alias_name = kvp.second.data();
|
for (auto &kvp : section.second) {
|
||||||
else if (kvp.first == "renamed_from") {
|
if (kvp.first == "alias")
|
||||||
if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) {
|
alias_name = kvp.second.data();
|
||||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" <<
|
else if (kvp.first == "renamed_from") {
|
||||||
section.first << "\" contains invalid \"renamed_from\" key, which is being ignored.";
|
if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) {
|
||||||
}
|
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" <<
|
||||||
}
|
section.first << "\" contains invalid \"renamed_from\" key, which is being ignored.";
|
||||||
config.set_deserialize(kvp.first, kvp.second.data());
|
}
|
||||||
|
}
|
||||||
|
// Throws on parsing error. For system presets, no substituion is being done, but an exception is thrown.
|
||||||
|
config.set_deserialize(kvp.first, kvp.second.data(), substitution_context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (presets == &this->printers) {
|
||||||
|
// Select the default config based on the printer_technology field extracted from kvp.
|
||||||
|
DynamicPrintConfig config_src;
|
||||||
|
parse_config_section(config_src);
|
||||||
|
default_config = &presets->default_preset_for(config_src).config;
|
||||||
|
config = *default_config;
|
||||||
|
config.apply(config_src);
|
||||||
|
} else {
|
||||||
|
default_config = &presets->default_preset().config;
|
||||||
|
config = *default_config;
|
||||||
|
parse_config_section(config);
|
||||||
}
|
}
|
||||||
};
|
} catch (const ConfigurationError &e) {
|
||||||
if (presets == &this->printers) {
|
throw ConfigurationError(format("Invalid configuration bundle \"%1%\", section [%2%]: ", path, section.first) + e.what());
|
||||||
// Select the default config based on the printer_technology field extracted from kvp.
|
|
||||||
DynamicPrintConfig config_src;
|
|
||||||
parse_config_section(config_src);
|
|
||||||
default_config = &presets->default_preset_for(config_src).config;
|
|
||||||
config = *default_config;
|
|
||||||
config.apply(config_src);
|
|
||||||
} else {
|
|
||||||
default_config = &presets->default_preset().config;
|
|
||||||
config = *default_config;
|
|
||||||
parse_config_section(config);
|
|
||||||
}
|
}
|
||||||
Preset::normalize(config);
|
Preset::normalize(config);
|
||||||
// Report configuration fields, which are misplaced into a wrong group.
|
// Report configuration fields, which are misplaced into a wrong group.
|
||||||
@ -1256,7 +1298,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
if (! incorrect_keys.empty())
|
if (! incorrect_keys.empty())
|
||||||
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The printer preset \"" <<
|
||||||
section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
section.first << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||||
if ((flags & LOAD_CFGBNDLE_SYSTEM) && presets == &printers) {
|
if (flags.has(LoadConfigBundleAttribute::LoadSystem) && presets == &printers) {
|
||||||
// Filter out printer presets, which are not mentioned in the vendor profile.
|
// Filter out printer presets, which are not mentioned in the vendor profile.
|
||||||
// These presets are considered not installed.
|
// These presets are considered not installed.
|
||||||
auto printer_model = config.opt_string("printer_model");
|
auto printer_model = config.opt_string("printer_model");
|
||||||
@ -1291,7 +1333,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
section.first << "\" has already been loaded from another Confing Bundle.";
|
section.first << "\" has already been loaded from another Confing Bundle.";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) {
|
} else if (! flags.has(LoadConfigBundleAttribute::LoadSystem)) {
|
||||||
// This is a user config bundle.
|
// This is a user config bundle.
|
||||||
const Preset *existing = presets->find_preset(preset_name, false);
|
const Preset *existing = presets->find_preset(preset_name, false);
|
||||||
if (existing != nullptr) {
|
if (existing != nullptr) {
|
||||||
@ -1320,9 +1362,9 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
/ presets->section_name() / file_name).make_preferred();
|
/ presets->section_name() / file_name).make_preferred();
|
||||||
// Load the preset into the list of presets, save it to disk.
|
// Load the preset into the list of presets, save it to disk.
|
||||||
Preset &loaded = presets->load_preset(file_path.string(), preset_name, std::move(config), false);
|
Preset &loaded = presets->load_preset(file_path.string(), preset_name, std::move(config), false);
|
||||||
if (flags & LOAD_CFGBNDLE_SAVE)
|
if (flags.has(LoadConfigBundleAttribute::SaveImported))
|
||||||
loaded.save();
|
loaded.save();
|
||||||
if (flags & LOAD_CFGBNDLE_SYSTEM) {
|
if (flags.has(LoadConfigBundleAttribute::LoadSystem)) {
|
||||||
loaded.is_system = true;
|
loaded.is_system = true;
|
||||||
loaded.vendor = vendor_profile;
|
loaded.vendor = vendor_profile;
|
||||||
}
|
}
|
||||||
@ -1343,7 +1385,10 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
else
|
else
|
||||||
loaded.alias = std::move(alias_name);
|
loaded.alias = std::move(alias_name);
|
||||||
loaded.renamed_from = std::move(renamed_from);
|
loaded.renamed_from = std::move(renamed_from);
|
||||||
|
if (! substitution_context.empty())
|
||||||
|
substitutions.push_back({
|
||||||
|
preset_name, presets->type(), PresetConfigSubstitutions::Source::ConfigBundle,
|
||||||
|
std::string(), std::move(substitution_context.substitutions) });
|
||||||
++ presets_loaded;
|
++ presets_loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1352,8 +1397,13 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
const DynamicPrintConfig& default_config = ph_printers->default_config();
|
const DynamicPrintConfig& default_config = ph_printers->default_config();
|
||||||
DynamicPrintConfig config = default_config;
|
DynamicPrintConfig config = default_config;
|
||||||
|
|
||||||
for (auto& kvp : section.second)
|
substitution_context.substitutions.clear();
|
||||||
config.set_deserialize(kvp.first, kvp.second.data());
|
try {
|
||||||
|
for (auto& kvp : section.second)
|
||||||
|
config.set_deserialize(kvp.first, kvp.second.data(), substitution_context);
|
||||||
|
} catch (const ConfigurationError &e) {
|
||||||
|
throw ConfigurationError(format("Invalid configuration bundle \"%1%\", section [%2%]: ", path, section.first) + e.what());
|
||||||
|
}
|
||||||
|
|
||||||
// Report configuration fields, which are misplaced into a wrong group.
|
// Report configuration fields, which are misplaced into a wrong group.
|
||||||
std::string incorrect_keys = Preset::remove_invalid_keys(config, default_config);
|
std::string incorrect_keys = Preset::remove_invalid_keys(config, default_config);
|
||||||
@ -1379,14 +1429,17 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
#endif
|
#endif
|
||||||
/ "physical_printer" / file_name).make_preferred();
|
/ "physical_printer" / file_name).make_preferred();
|
||||||
// Load the preset into the list of presets, save it to disk.
|
// Load the preset into the list of presets, save it to disk.
|
||||||
ph_printers->load_printer(file_path.string(), ph_printer_name, std::move(config), false, flags & LOAD_CFGBNDLE_SAVE);
|
ph_printers->load_printer(file_path.string(), ph_printer_name, std::move(config), false, flags.has(LoadConfigBundleAttribute::SaveImported));
|
||||||
|
if (! substitution_context.empty())
|
||||||
++ph_printers_loaded;
|
substitutions.push_back({
|
||||||
|
ph_printer_name, Preset::TYPE_PHYSICAL_PRINTER, PresetConfigSubstitutions::Source::ConfigBundle,
|
||||||
|
std::string(), std::move(substitution_context.substitutions) });
|
||||||
|
++ ph_printers_loaded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Activate the presets and physical printer if any exists.
|
// 3) Activate the presets and physical printer if any exists.
|
||||||
if ((flags & LOAD_CFGBNDLE_SYSTEM) == 0) {
|
if (! flags.has(LoadConfigBundleAttribute::LoadSystem)) {
|
||||||
if (! active_print.empty())
|
if (! active_print.empty())
|
||||||
prints.select_preset_by_name(active_print, true);
|
prints.select_preset_by_name(active_print, true);
|
||||||
if (! active_sla_print.empty())
|
if (! active_sla_print.empty())
|
||||||
@ -1406,7 +1459,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
|
|||||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
return presets_loaded + ph_printers_loaded;
|
return std::make_pair(std::move(substitutions), presets_loaded + ph_printers_loaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetBundle::update_multi_material_filament_presets()
|
void PresetBundle::update_multi_material_filament_presets()
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "Preset.hpp"
|
#include "Preset.hpp"
|
||||||
#include "AppConfig.hpp"
|
#include "AppConfig.hpp"
|
||||||
|
#include "enum_bitmask.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -26,7 +27,7 @@ public:
|
|||||||
// Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets.
|
// Load ini files of all types (print, filament, printer) from Slic3r::data_dir() / presets.
|
||||||
// Load selections (current print, current filaments, current printer) from config.ini
|
// Load selections (current print, current filaments, current printer) from config.ini
|
||||||
// This is done just once on application start up.
|
// This is done just once on application start up.
|
||||||
void load_presets(AppConfig &config, const std::string &preferred_model_id = "");
|
PresetsConfigSubstitutions load_presets(AppConfig &config, ForwardCompatibilitySubstitutionRule rule, const std::string &preferred_model_id = "");
|
||||||
|
|
||||||
// Export selections (current print, current filaments, current printer) into config.ini
|
// Export selections (current print, current filaments, current printer) into config.ini
|
||||||
void export_selections(AppConfig &config);
|
void export_selections(AppConfig &config);
|
||||||
@ -82,24 +83,27 @@ public:
|
|||||||
// Instead of a config file, a G-code may be loaded containing the full set of parameters.
|
// Instead of a config file, a G-code may be loaded containing the full set of parameters.
|
||||||
// In the future the configuration will likely be read from an AMF file as well.
|
// In the future the configuration will likely be read from an AMF file as well.
|
||||||
// If the file is loaded successfully, its print / filament / printer profiles will be activated.
|
// If the file is loaded successfully, its print / filament / printer profiles will be activated.
|
||||||
void load_config_file(const std::string &path);
|
ConfigSubstitutions load_config_file(const std::string &path, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
|
|
||||||
// Load a config bundle file, into presets and store the loaded presets into separate files
|
// Load a config bundle file, into presets and store the loaded presets into separate files
|
||||||
// of the local configuration directory.
|
// of the local configuration directory.
|
||||||
// Load settings into the provided settings instance.
|
// Load settings into the provided settings instance.
|
||||||
// Activate the presets stored in the config bundle.
|
// Activate the presets stored in the config bundle.
|
||||||
// Returns the number of presets loaded successfully.
|
// Returns the number of presets loaded successfully.
|
||||||
enum {
|
enum LoadConfigBundleAttribute {
|
||||||
// Save the profiles, which have been loaded.
|
// Save the profiles, which have been loaded.
|
||||||
LOAD_CFGBNDLE_SAVE = 1,
|
SaveImported,
|
||||||
// Delete all old config profiles before loading.
|
// Delete all old config profiles before loading.
|
||||||
LOAD_CFGBNDLE_RESET_USER_PROFILE = 2,
|
ResetUserProfile,
|
||||||
// Load a system config bundle.
|
// Load a system config bundle.
|
||||||
LOAD_CFGBNDLE_SYSTEM = 4,
|
LoadSystem,
|
||||||
LOAD_CFGBUNDLE_VENDOR_ONLY = 8,
|
LoadVendorOnly,
|
||||||
};
|
};
|
||||||
// Load the config bundle, store it to the user profile directory by default.
|
using LoadConfigBundleAttributes = enum_bitmask<LoadConfigBundleAttribute>;
|
||||||
size_t load_configbundle(const std::string &path, unsigned int flags = LOAD_CFGBNDLE_SAVE);
|
// Load the config bundle based on the flags.
|
||||||
|
// Don't do any config substitutions when loading a system profile, perform and report substitutions otherwise.
|
||||||
|
std::pair<PresetsConfigSubstitutions, size_t> load_configbundle(
|
||||||
|
const std::string &path, LoadConfigBundleAttributes flags, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
|
|
||||||
// Export a config bundle file containing all the presets and the names of the active presets.
|
// Export a config bundle file containing all the presets and the names of the active presets.
|
||||||
void export_configbundle(const std::string &path, bool export_system_settings = false, bool export_physical_printers = false);
|
void export_configbundle(const std::string &path, bool export_system_settings = false, bool export_physical_printers = false);
|
||||||
@ -136,7 +140,7 @@ public:
|
|||||||
|
|
||||||
static const char *PRUSA_BUNDLE;
|
static const char *PRUSA_BUNDLE;
|
||||||
private:
|
private:
|
||||||
std::string load_system_presets();
|
std::pair<PresetsConfigSubstitutions, std::string> load_system_presets(ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
// Merge one vendor's presets with the other vendor's presets, report duplicates.
|
||||||
std::vector<std::string> merge_presets(PresetBundle &&other);
|
std::vector<std::string> merge_presets(PresetBundle &&other);
|
||||||
// Update renamed_from and alias maps of system profiles.
|
// Update renamed_from and alias maps of system profiles.
|
||||||
@ -155,12 +159,15 @@ private:
|
|||||||
// and the external config is just referenced, not stored into user profile directory.
|
// and the external config is just referenced, not stored into user profile directory.
|
||||||
// If it is not an external config, then the config will be stored into the user profile directory.
|
// If it is not an external config, then the config will be stored into the user profile directory.
|
||||||
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config);
|
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config);
|
||||||
void load_config_file_config_bundle(const std::string &path, const boost::property_tree::ptree &tree);
|
ConfigSubstitutions load_config_file_config_bundle(
|
||||||
|
const std::string &path, const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||||
|
|
||||||
DynamicPrintConfig full_fff_config() const;
|
DynamicPrintConfig full_fff_config() const;
|
||||||
DynamicPrintConfig full_sla_config() const;
|
DynamicPrintConfig full_sla_config() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ENABLE_ENUM_BITMASK_OPERATORS(PresetBundle::LoadConfigBundleAttribute)
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif /* slic3r_PresetBundle_hpp_ */
|
#endif /* slic3r_PresetBundle_hpp_ */
|
||||||
|
@ -74,7 +74,7 @@ void PrintConfigDef::init_common_params()
|
|||||||
|
|
||||||
def = this->add("thumbnails", coPoints);
|
def = this->add("thumbnails", coPoints);
|
||||||
def->label = L("Thumbnails size");
|
def->label = L("Thumbnails size");
|
||||||
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files, in the following format: \"XxY, XxY, ...\"");
|
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\"");
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
def->max = 2048;
|
def->max = 2048;
|
||||||
@ -2723,12 +2723,14 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
|
def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
|
||||||
"the kind of the host.");
|
"the kind of the host.");
|
||||||
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
|
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
|
||||||
|
def->enum_values.push_back("prusalink");
|
||||||
def->enum_values.push_back("octoprint");
|
def->enum_values.push_back("octoprint");
|
||||||
def->enum_values.push_back("duet");
|
def->enum_values.push_back("duet");
|
||||||
def->enum_values.push_back("flashair");
|
def->enum_values.push_back("flashair");
|
||||||
def->enum_values.push_back("astrobox");
|
def->enum_values.push_back("astrobox");
|
||||||
def->enum_values.push_back("repetier");
|
def->enum_values.push_back("repetier");
|
||||||
def->enum_values.push_back("klipper");
|
def->enum_values.push_back("klipper");
|
||||||
|
def->enum_labels.push_back("PrusaLink");
|
||||||
def->enum_labels.push_back("OctoPrint");
|
def->enum_labels.push_back("OctoPrint");
|
||||||
def->enum_labels.push_back("Duet");
|
def->enum_labels.push_back("Duet");
|
||||||
def->enum_labels.push_back("FlashAir");
|
def->enum_labels.push_back("FlashAir");
|
||||||
@ -5283,7 +5285,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||||||
} else if (opt_key == "bed_size" && !value.empty()) {
|
} else if (opt_key == "bed_size" && !value.empty()) {
|
||||||
opt_key = "bed_shape";
|
opt_key = "bed_shape";
|
||||||
ConfigOptionPoint p;
|
ConfigOptionPoint p;
|
||||||
p.deserialize(value);
|
p.deserialize(value, ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "0x0," << p.value(0) << "x0," << p.value(0) << "x" << p.value(1) << ",0x" << p.value(1);
|
oss << "0x0," << p.value(0) << "x0," << p.value(0) << "x" << p.value(1) << ",0x" << p.value(1);
|
||||||
value = oss.str();
|
value = oss.str();
|
||||||
@ -6528,6 +6530,20 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
|||||||
def->label = L("Ignore non-existent config files");
|
def->label = L("Ignore non-existent config files");
|
||||||
def->tooltip = L("Do not fail if a file supplied to --load does not exist.");
|
def->tooltip = L("Do not fail if a file supplied to --load does not exist.");
|
||||||
|
|
||||||
|
def = this->add("config_compatibility", coEnum);
|
||||||
|
def->label = L("Forward-compatibility rule when loading configurations from config files and project files (3MF, AMF).");
|
||||||
|
def->tooltip = L("This version of Slic3r may not understand configurations produced by newest Slic3r versions. "
|
||||||
|
"For example, newer Slic3r may extend the list of supported firmware flavors. One may decide to "
|
||||||
|
"bail out or to substitute an unknown value with a default silently or verbosely.");
|
||||||
|
def->enum_keys_map = &ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>::get_enum_values();
|
||||||
|
def->enum_values.push_back("disable");
|
||||||
|
def->enum_values.push_back("enable");
|
||||||
|
def->enum_values.push_back("enable_silent");
|
||||||
|
def->enum_labels.push_back("Bail out on unknown configuration values");
|
||||||
|
def->enum_labels.push_back("Enable reading unknown configuration values by verbosely substituting them with defaults.");
|
||||||
|
def->enum_labels.push_back("Enable reading unknown configuration values by silently substituting them with defaults.");
|
||||||
|
def->set_default_value(new ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>(ForwardCompatibilitySubstitutionRule::Enable));
|
||||||
|
|
||||||
def = this->add("load", coStrings);
|
def = this->add("load", coStrings);
|
||||||
def->label = L("Load config file");
|
def->label = L("Load config file");
|
||||||
def->tooltip = L("Load configuration from the specified file. It can be used more than once to load options from multiple files.");
|
def->tooltip = L("Load configuration from the specified file. It can be used more than once to load options from multiple files.");
|
||||||
|
@ -72,6 +72,7 @@ enum class MachineLimitsUsage : uint8_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum PrintHostType {
|
enum PrintHostType {
|
||||||
|
htPrusaLink,
|
||||||
htOctoPrint,
|
htOctoPrint,
|
||||||
htDuet,
|
htDuet,
|
||||||
htFlashAir,
|
htFlashAir,
|
||||||
@ -222,6 +223,7 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<MachineLimitsUsag
|
|||||||
|
|
||||||
template<> inline const t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
|
template<> inline const t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
|
||||||
static t_config_enum_values keys_map = {
|
static t_config_enum_values keys_map = {
|
||||||
|
{"prusalink", htPrusaLink},
|
||||||
{"octoprint", htOctoPrint},
|
{"octoprint", htOctoPrint},
|
||||||
{"duet", htDuet},
|
{"duet", htDuet},
|
||||||
{"flashair", htFlashAir},
|
{"flashair", htFlashAir},
|
||||||
@ -370,6 +372,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<ZLiftTop>::get_en
|
|||||||
return keys_map;
|
return keys_map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> inline const t_config_enum_values& ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>::get_enum_values() {
|
||||||
|
static const t_config_enum_values keys_map = {
|
||||||
|
{ "disable", ForwardCompatibilitySubstitutionRule::Disable },
|
||||||
|
{ "enable", ForwardCompatibilitySubstitutionRule::Enable },
|
||||||
|
{ "enable_silent", ForwardCompatibilitySubstitutionRule::EnableSilent }
|
||||||
|
};
|
||||||
|
|
||||||
|
return keys_map;
|
||||||
|
}
|
||||||
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
|
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
|
||||||
// Does not store the actual values, but defines default values.
|
// Does not store the actual values, but defines default values.
|
||||||
class PrintConfigDef : public ConfigDef
|
class PrintConfigDef : public ConfigDef
|
||||||
@ -1901,8 +1912,8 @@ public:
|
|||||||
bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; }
|
bool set_key_value(const std::string &opt_key, ConfigOption *opt) { bool out = m_data.set_key_value(opt_key, opt); this->touch(); return out; }
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); }
|
void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); }
|
||||||
void set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false)
|
void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false)
|
||||||
{ m_data.set_deserialize(opt_key, str, append); this->touch(); }
|
{ m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); }
|
||||||
bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; }
|
bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; }
|
||||||
|
|
||||||
// Getters are thread safe.
|
// Getters are thread safe.
|
||||||
|
@ -2925,6 +2925,16 @@ namespace Slic3r {
|
|||||||
return this->slice_volumes(zs, SlicingMode::Regular, volumes);
|
return this->slice_volumes(zs, SlicingMode::Regular, volumes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
||||||
|
static void fix_mesh_connectivity(TriangleMesh &mesh)
|
||||||
|
{
|
||||||
|
auto nr_degenerated = mesh.stl.stats.degenerate_facets;
|
||||||
|
stl_check_facets_exact(&mesh.stl);
|
||||||
|
if (nr_degenerated != mesh.stl.stats.degenerate_facets)
|
||||||
|
// stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation.
|
||||||
|
stl_generate_shared_vertices(&mesh.stl, mesh.its);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<ExPolygons> PrintObject::slice_volumes(
|
std::vector<ExPolygons> PrintObject::slice_volumes(
|
||||||
const std::vector<float>& z,
|
const std::vector<float>& z,
|
||||||
SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below,
|
SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below,
|
||||||
@ -2937,10 +2947,8 @@ namespace Slic3r {
|
|||||||
TriangleMesh mesh(volumes.front()->mesh());
|
TriangleMesh mesh(volumes.front()->mesh());
|
||||||
mesh.transform(volumes.front()->get_matrix(), true);
|
mesh.transform(volumes.front()->get_matrix(), true);
|
||||||
assert(mesh.repaired);
|
assert(mesh.repaired);
|
||||||
if (volumes.size() == 1 && mesh.repaired) {
|
if (volumes.size() == 1 && mesh.repaired)
|
||||||
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
fix_mesh_connectivity(mesh);
|
||||||
stl_check_facets_exact(&mesh.stl);
|
|
||||||
}
|
|
||||||
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++idx_volume) {
|
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++idx_volume) {
|
||||||
const ModelVolume& model_volume = *volumes[idx_volume];
|
const ModelVolume& model_volume = *volumes[idx_volume];
|
||||||
TriangleMesh vol_mesh(model_volume.mesh());
|
TriangleMesh vol_mesh(model_volume.mesh());
|
||||||
@ -2973,10 +2981,8 @@ namespace Slic3r {
|
|||||||
//FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
|
//FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
|
||||||
TriangleMesh mesh(volume.mesh());
|
TriangleMesh mesh(volume.mesh());
|
||||||
mesh.transform(volume.get_matrix(), true);
|
mesh.transform(volume.get_matrix(), true);
|
||||||
if (mesh.repaired) {
|
if (mesh.repaired)
|
||||||
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
|
fix_mesh_connectivity(mesh);
|
||||||
stl_check_facets_exact(&mesh.stl);
|
|
||||||
}
|
|
||||||
if (mesh.stl.stats.number_of_facets > 0) {
|
if (mesh.stl.stats.number_of_facets > 0) {
|
||||||
mesh.transform(m_trafo, true);
|
mesh.transform(m_trafo, true);
|
||||||
// apply XY shift
|
// apply XY shift
|
||||||
|
@ -1547,6 +1547,11 @@ static inline void do_crossover(const std::vector<FlipEdge> &edges_in, std::vect
|
|||||||
assert(edges_in.size() == edges_out.size());
|
assert(edges_in.size() == edges_out.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Worst time complexity: O(min(n, 100) * (n * log n + n^2)
|
||||||
|
// Expected time complexity: O(min(n, 100) * (n * log n + k * n)
|
||||||
|
// where n is the number of edges and k is the number of connection_lengths candidates after the first one
|
||||||
|
// is found that improves the total cost.
|
||||||
|
//FIXME there are likley better heuristics to lower the time complexity.
|
||||||
static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector<FlipEdge> &edges)
|
static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector<FlipEdge> &edges)
|
||||||
{
|
{
|
||||||
if (edges.size() < 2)
|
if (edges.size() < 2)
|
||||||
@ -1556,7 +1561,8 @@ static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector<Fl
|
|||||||
std::vector<FlipEdge> edges_tmp(edges);
|
std::vector<FlipEdge> edges_tmp(edges);
|
||||||
std::vector<std::pair<double, size_t>> connection_lengths(edges.size() - 1, std::pair<double, size_t>(0., 0));
|
std::vector<std::pair<double, size_t>> connection_lengths(edges.size() - 1, std::pair<double, size_t>(0., 0));
|
||||||
std::vector<char> connection_tried(edges.size(), false);
|
std::vector<char> connection_tried(edges.size(), false);
|
||||||
for (size_t iter = 0; iter < edges.size(); ++ iter) {
|
const size_t max_iterations = std::min(edges.size(), size_t(100));
|
||||||
|
for (size_t iter = 0; iter < max_iterations; ++ iter) {
|
||||||
// Initialize connection costs and connection lengths.
|
// Initialize connection costs and connection lengths.
|
||||||
for (size_t i = 1; i < edges.size(); ++ i) {
|
for (size_t i = 1; i < edges.size(); ++ i) {
|
||||||
const FlipEdge &e1 = edges[i - 1];
|
const FlipEdge &e1 = edges[i - 1];
|
||||||
@ -1621,6 +1627,8 @@ static inline void reorder_by_two_exchanges_with_segment_flipping(std::vector<Fl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Currently not used, too slow.
|
||||||
static inline void reorder_by_three_exchanges_with_segment_flipping(std::vector<FlipEdge> &edges)
|
static inline void reorder_by_three_exchanges_with_segment_flipping(std::vector<FlipEdge> &edges)
|
||||||
{
|
{
|
||||||
if (edges.size() < 3) {
|
if (edges.size() < 3) {
|
||||||
@ -1703,6 +1711,7 @@ static inline void reorder_by_three_exchanges_with_segment_flipping(std::vector<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign> Matrixd;
|
typedef Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign> Matrixd;
|
||||||
|
|
||||||
@ -1774,6 +1783,8 @@ static inline std::pair<double, size_t> minimum_crossover_cost(
|
|||||||
return std::make_pair(cost_min, flip_min);
|
return std::make_pair(cost_min, flip_min);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Currently not used, too slow.
|
||||||
static inline void reorder_by_three_exchanges_with_segment_flipping2(std::vector<FlipEdge> &edges)
|
static inline void reorder_by_three_exchanges_with_segment_flipping2(std::vector<FlipEdge> &edges)
|
||||||
{
|
{
|
||||||
if (edges.size() < 3) {
|
if (edges.size() < 3) {
|
||||||
@ -1870,8 +1881,11 @@ static inline void reorder_by_three_exchanges_with_segment_flipping2(std::vector
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Flip the sequences of polylines to lower the total length of connecting lines.
|
// Flip the sequences of polylines to lower the total length of connecting lines.
|
||||||
|
// Used by the infill generator if the infill is not connected with perimeter lines
|
||||||
|
// and to order the brim lines.
|
||||||
static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polylines &polylines, bool fixed_start)
|
static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polylines &polylines, bool fixed_start)
|
||||||
{
|
{
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@ -1923,6 +1937,7 @@ static inline void improve_ordering_by_two_exchanges_with_segment_flipping(Polyl
|
|||||||
#endif /* NDEBUG */
|
#endif /* NDEBUG */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to optimize order of infill lines and brim lines.
|
||||||
Polylines chain_polylines(Polylines &&polylines, const Point *start_near)
|
Polylines chain_polylines(Polylines &&polylines, const Point *start_near)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG_SVG_OUTPUT
|
#ifdef DEBUG_SVG_OUTPUT
|
||||||
|
@ -226,7 +226,7 @@ inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER
|
|||||||
return container[next_idx_modulo(idx, container.size())];
|
return container[next_idx_modulo(idx, container.size())];
|
||||||
}
|
}
|
||||||
|
|
||||||
extern std::string xml_escape(std::string text);
|
extern std::string xml_escape(std::string text, bool is_marked = false);
|
||||||
|
|
||||||
|
|
||||||
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__
|
#if defined __GNUC__ && __GNUC__ < 5 && !defined __clang__
|
||||||
|
80
src/libslic3r/enum_bitmask.hpp
Normal file
80
src/libslic3r/enum_bitmask.hpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#ifndef slic3r_enum_bitmask_hpp_
|
||||||
|
#define slic3r_enum_bitmask_hpp_
|
||||||
|
|
||||||
|
// enum_bitmask for passing a set of attributes to a function in a type safe way.
|
||||||
|
// Adapted from https://gpfault.net/posts/typesafe-bitmasks.txt.html
|
||||||
|
// with hints from https://www.strikerx3.dev/cpp/2019/02/27/typesafe-enum-class-bitmasks-in-cpp.html
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
// enum_bitmasks can only be used with enums.
|
||||||
|
template<class option_type, typename = typename std::enable_if<std::is_enum<option_type>::value>::type>
|
||||||
|
class enum_bitmask {
|
||||||
|
// The type we'll use for storing the value of our bitmask should be the same as the enum's underlying type.
|
||||||
|
using underlying_type = typename std::underlying_type<option_type>::type;
|
||||||
|
|
||||||
|
// This method helps us avoid having to explicitly set enum values to powers of two.
|
||||||
|
static constexpr underlying_type mask_value(option_type o) { return 1 << static_cast<underlying_type>(o); }
|
||||||
|
|
||||||
|
// Private ctor to be used internally.
|
||||||
|
explicit constexpr enum_bitmask(underlying_type o) : m_bits(o) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Default ctor creates a bitmask with no options selected.
|
||||||
|
constexpr enum_bitmask() : m_bits(0) {}
|
||||||
|
|
||||||
|
// Creates a enum_bitmask with just one bit set.
|
||||||
|
// This ctor is intentionally non-explicit, to allow passing an options to a function:
|
||||||
|
// FunctionExpectingBitmask(Options::Opt1)
|
||||||
|
constexpr enum_bitmask(option_type o) : m_bits(mask_value(o)) {}
|
||||||
|
|
||||||
|
// Set the bit corresponding to the given option.
|
||||||
|
constexpr enum_bitmask operator|(option_type t) { return enum_bitmask(m_bits | mask_value(t)); }
|
||||||
|
|
||||||
|
// Combine with another enum_bitmask of the same type.
|
||||||
|
constexpr enum_bitmask operator|(enum_bitmask<option_type> t) { return enum_bitmask(m_bits | t.m_bits); }
|
||||||
|
|
||||||
|
// Get the value of the bit corresponding to the given option.
|
||||||
|
constexpr bool operator&(option_type t) { return m_bits & mask_value(t); }
|
||||||
|
constexpr bool has(option_type t) { return m_bits & mask_value(t); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
underlying_type m_bits = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// For enabling free functions producing enum_bitmask<> type from bit operations on enums.
|
||||||
|
template<typename Enum> struct is_enum_bitmask_type { static const bool enable = false; };
|
||||||
|
#define ENABLE_ENUM_BITMASK_OPERATORS(x) template<> struct is_enum_bitmask_type<x> { static const bool enable = true; };
|
||||||
|
template<class Enum> inline constexpr bool is_enum_bitmask_type_v = is_enum_bitmask_type<Enum>::enable;
|
||||||
|
|
||||||
|
// Creates an enum_bitmask from two options, convenient for passing of options to a function:
|
||||||
|
// FunctionExpectingBitmask(Options::Opt1 | Options::Opt2 | Options::Opt3)
|
||||||
|
template <class option_type>
|
||||||
|
constexpr std::enable_if_t<is_enum_bitmask_type_v<option_type>, enum_bitmask<option_type>> operator|(option_type lhs, option_type rhs) {
|
||||||
|
static_assert(std::is_enum_v<option_type>);
|
||||||
|
return enum_bitmask<option_type>{lhs} | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class option_type>
|
||||||
|
constexpr std::enable_if_t<is_enum_bitmask_type_v<option_type>, enum_bitmask<option_type>> operator|(option_type lhs, enum_bitmask<option_type> rhs) {
|
||||||
|
static_assert(std::is_enum_v<option_type>);
|
||||||
|
return enum_bitmask<option_type>{lhs} | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class option_type>
|
||||||
|
constexpr std::enable_if_t<is_enum_bitmask_type_v<option_type>, enum_bitmask<option_type>> only_if(bool condition, option_type opt) {
|
||||||
|
static_assert(std::is_enum_v<option_type>);
|
||||||
|
return condition ? enum_bitmask<option_type>{opt} : enum_bitmask<option_type>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class option_type>
|
||||||
|
constexpr std::enable_if_t<is_enum_bitmask_type_v<option_type>, enum_bitmask<option_type>> only_if(bool condition, enum_bitmask<option_type> opt) {
|
||||||
|
static_assert(std::is_enum_v<option_type>);
|
||||||
|
return condition ? opt : enum_bitmask<option_type>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_enum_bitmask_hpp_
|
@ -111,6 +111,7 @@
|
|||||||
#include "BoundingBox.hpp"
|
#include "BoundingBox.hpp"
|
||||||
#include "ClipperUtils.hpp"
|
#include "ClipperUtils.hpp"
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
|
#include "enum_bitmask.hpp"
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "MultiPoint.hpp"
|
#include "MultiPoint.hpp"
|
||||||
|
@ -424,6 +424,53 @@ std::error_code rename_file(const std::string &from, const std::string &to)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
// Copied from boost::filesystem.
|
||||||
|
// Called by copy_file_linux() in case linux sendfile() API is not supported.
|
||||||
|
int copy_file_linux_read_write(int infile, int outfile, uintmax_t file_size)
|
||||||
|
{
|
||||||
|
std::vector<char> buf(
|
||||||
|
// Prefer the buffer to be larger than the file size so that we don't have
|
||||||
|
// to perform an extra read if the file fits in the buffer exactly.
|
||||||
|
std::clamp<size_t>(file_size + (file_size < ~static_cast<uintmax_t >(0u)),
|
||||||
|
// Min and max buffer sizes are selected to minimize the overhead from system calls.
|
||||||
|
// The values are picked based on coreutils cp(1) benchmarking data described here:
|
||||||
|
// https://github.com/coreutils/coreutils/blob/d1b0257077c0b0f0ee25087efd46270345d1dd1f/src/ioblksize.h#L23-L72
|
||||||
|
8u * 1024u, 256u * 1024u),
|
||||||
|
0);
|
||||||
|
|
||||||
|
#if defined(POSIX_FADV_SEQUENTIAL)
|
||||||
|
::posix_fadvise(infile, 0, 0, POSIX_FADV_SEQUENTIAL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Don't use file size to limit the amount of data to copy since some filesystems, like procfs or sysfs,
|
||||||
|
// provide files with generated content and indicate that their size is zero or 4096. Just copy as much data
|
||||||
|
// as we can read from the input file.
|
||||||
|
while (true) {
|
||||||
|
ssize_t sz_read = ::read(infile, buf.data(), buf.size());
|
||||||
|
if (sz_read == 0)
|
||||||
|
break;
|
||||||
|
if (sz_read < 0) {
|
||||||
|
int err = errno;
|
||||||
|
if (err == EINTR)
|
||||||
|
continue;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// Allow for partial writes - see Advanced Unix Programming (2nd Ed.),
|
||||||
|
// Marc Rochkind, Addison-Wesley, 2004, page 94
|
||||||
|
for (ssize_t sz_wrote = 0; sz_wrote < sz_read;) {
|
||||||
|
ssize_t sz = ::write(outfile, buf.data() + sz_wrote, static_cast<std::size_t>(sz_read - sz_wrote));
|
||||||
|
if (sz < 0) {
|
||||||
|
int err = errno;
|
||||||
|
if (err == EINTR)
|
||||||
|
continue;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
sz_wrote += sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Copied from boost::filesystem, to support copying a file to a weird filesystem, which does not support changing file attributes,
|
// Copied from boost::filesystem, to support copying a file to a weird filesystem, which does not support changing file attributes,
|
||||||
// for example ChromeOS Linux integration or FlashAIR WebDAV.
|
// for example ChromeOS Linux integration or FlashAIR WebDAV.
|
||||||
// Copied and simplified from boost::filesystem::detail::copy_file() with option = overwrite_if_exists and with just the Linux path kept,
|
// Copied and simplified from boost::filesystem::detail::copy_file() with option = overwrite_if_exists and with just the Linux path kept,
|
||||||
@ -520,6 +567,19 @@ bool copy_file_linux(const boost::filesystem::path &from, const boost::filesyste
|
|||||||
ssize_t sz = ::sendfile(outfile.fd, infile.fd, nullptr, size_to_copy);
|
ssize_t sz = ::sendfile(outfile.fd, infile.fd, nullptr, size_to_copy);
|
||||||
if (sz < 0) {
|
if (sz < 0) {
|
||||||
err = errno;
|
err = errno;
|
||||||
|
if (offset == 0u) {
|
||||||
|
// sendfile may fail with EINVAL if the underlying filesystem does not support it.
|
||||||
|
// See https://patchwork.kernel.org/project/linux-nfs/patch/20190411183418.4510-1-olga.kornievskaia@gmail.com/
|
||||||
|
// https://bugzilla.redhat.com/show_bug.cgi?id=1783554.
|
||||||
|
// https://github.com/boostorg/filesystem/commit/4b9052f1e0b2acf625e8247582f44acdcc78a4ce
|
||||||
|
if (err == EINVAL || err == EOPNOTSUPP) {
|
||||||
|
err = copy_file_linux_read_write(infile.fd, outfile.fd, from_stat.st_size);
|
||||||
|
if (err < 0)
|
||||||
|
goto fail;
|
||||||
|
// Succeeded.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (err == EINTR)
|
if (err == EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (err == 0)
|
if (err == 0)
|
||||||
@ -529,7 +589,6 @@ bool copy_file_linux(const boost::filesystem::path &from, const boost::filesyste
|
|||||||
offset += sz;
|
offset += sz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we created a new file with an explicitly added S_IWUSR permission,
|
// If we created a new file with an explicitly added S_IWUSR permission,
|
||||||
// we may need to update its mode bits to match the source file.
|
// we may need to update its mode bits to match the source file.
|
||||||
if (to_mode != from_mode && ::fchmod(outfile.fd, from_mode) != 0) {
|
if (to_mode != from_mode && ::fchmod(outfile.fd, from_mode) != 0) {
|
||||||
@ -781,7 +840,7 @@ unsigned get_current_pid()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string xml_escape(std::string text)
|
std::string xml_escape(std::string text, bool is_marked/* = false*/)
|
||||||
{
|
{
|
||||||
std::string::size_type pos = 0;
|
std::string::size_type pos = 0;
|
||||||
for (;;)
|
for (;;)
|
||||||
@ -796,8 +855,8 @@ std::string xml_escape(std::string text)
|
|||||||
case '\"': replacement = """; break;
|
case '\"': replacement = """; break;
|
||||||
case '\'': replacement = "'"; break;
|
case '\'': replacement = "'"; break;
|
||||||
case '&': replacement = "&"; break;
|
case '&': replacement = "&"; break;
|
||||||
case '<': replacement = "<"; break;
|
case '<': replacement = is_marked ? "<" :"<"; break;
|
||||||
case '>': replacement = ">"; break;
|
case '>': replacement = is_marked ? ">" :">"; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,14 +8,23 @@
|
|||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
#include <boost/property_tree/ptree_fwd.hpp>
|
#include <boost/property_tree/ptree_fwd.hpp>
|
||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
#include "libslic3r/format.hpp"
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "libslic3r/Time.hpp"
|
#include "libslic3r/Time.hpp"
|
||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
#include "libslic3r/FileParserError.hpp"
|
#include "libslic3r/FileParserError.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
|
|
||||||
|
#include "../GUI/GUI.hpp"
|
||||||
|
#include "../GUI/GUI_App.hpp"
|
||||||
|
#include "../GUI/I18N.hpp"
|
||||||
|
#include "../GUI/MainFrame.hpp"
|
||||||
|
|
||||||
|
#include <wx/richmsgdlg.h>
|
||||||
|
|
||||||
#define SLIC3R_SNAPSHOTS_DIR "snapshots"
|
#define SLIC3R_SNAPSHOTS_DIR "snapshots"
|
||||||
#define SLIC3R_SNAPSHOT_FILE "snapshot.ini"
|
#define SLIC3R_SNAPSHOT_FILE "snapshot.ini"
|
||||||
|
|
||||||
@ -362,7 +371,8 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src
|
|||||||
|
|
||||||
for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
|
for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
|
||||||
if (Slic3r::is_ini_file(dir_entry))
|
if (Slic3r::is_ini_file(dir_entry))
|
||||||
boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists);
|
if (std::string error_message; copy_file(dir_entry.path().string(), (path_dst / dir_entry.path().filename()).string(), error_message, false) != SUCCESS)
|
||||||
|
throw Slic3r::RuntimeError(format("Failed copying \"%1%\" to \"%2%\": %3%", path_src.string(), path_dst.string(), error_message));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_existing_ini_files(const boost::filesystem::path &path)
|
static void delete_existing_ini_files(const boost::filesystem::path &path)
|
||||||
@ -413,7 +423,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
|
|||||||
++ it;
|
++ it;
|
||||||
// Read the active config bundle, parse the config version.
|
// Read the active config bundle, parse the config version.
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY);
|
bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LoadConfigBundleAttribute::LoadVendorOnly, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||||
for (const auto &vp : bundle.vendors)
|
for (const auto &vp : bundle.vendors)
|
||||||
if (vp.second.id == cfg.name)
|
if (vp.second.id == cfg.name)
|
||||||
cfg.version.config_version = vp.second.config_version;
|
cfg.version.config_version = vp.second.config_version;
|
||||||
@ -433,14 +443,27 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
|
|||||||
}
|
}
|
||||||
|
|
||||||
boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id;
|
boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id;
|
||||||
boost::filesystem::create_directory(snapshot_dir);
|
|
||||||
|
|
||||||
// Backup the presets.
|
try {
|
||||||
for (const char *subdir : snapshot_subdirs)
|
boost::filesystem::create_directory(snapshot_dir);
|
||||||
copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir);
|
|
||||||
snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
|
// Backup the presets.
|
||||||
assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured);
|
for (const char *subdir : snapshot_subdirs)
|
||||||
m_snapshots.emplace_back(std::move(snapshot));
|
copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir);
|
||||||
|
snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
|
||||||
|
assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured);
|
||||||
|
m_snapshots.emplace_back(std::move(snapshot));
|
||||||
|
} catch (...) {
|
||||||
|
if (boost::filesystem::is_directory(snapshot_dir)) {
|
||||||
|
try {
|
||||||
|
// Clean up partially copied snapshot.
|
||||||
|
boost::filesystem::remove_all(snapshot_dir);
|
||||||
|
} catch (...) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Failed taking snapshot and failed removing the snapshot directory " << snapshot_dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw;
|
||||||
|
}
|
||||||
return m_snapshots.back();
|
return m_snapshots.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,6 +574,32 @@ SnapshotDB& SnapshotDB::singleton()
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Snapshot* take_config_snapshot_report_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return &SnapshotDB::singleton().take_snapshot(app_config, reason, comment);
|
||||||
|
} catch (std::exception &err) {
|
||||||
|
show_error(static_cast<wxWindow*>(wxGetApp().mainframe),
|
||||||
|
_L("Taking a configuration snapshot failed.") + "\n\n" + from_u8(err.what()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment, const std::string &message)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
SnapshotDB::singleton().take_snapshot(app_config, reason, comment);
|
||||||
|
return true;
|
||||||
|
} catch (std::exception &err) {
|
||||||
|
wxRichMessageDialog dlg(static_cast<wxWindow*>(wxGetApp().mainframe),
|
||||||
|
_L("PrusaSlicer has encountered an error while taking a configuration snapshot.") + "\n\n" + from_u8(err.what()) + "\n\n" + from_u8(message),
|
||||||
|
_L("PrusaSlicer error"),
|
||||||
|
wxYES_NO);
|
||||||
|
dlg.SetYesNoLabels(_L("Continue"), _L("Abort"));
|
||||||
|
return dlg.ShowModal() == wxID_YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -127,6 +127,13 @@ private:
|
|||||||
std::vector<Snapshot> m_snapshots;
|
std::vector<Snapshot> m_snapshots;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Take snapshot on SnapshotDB::singleton(). If taking snapshot fails, report an error and return nullptr.
|
||||||
|
const Snapshot* take_config_snapshot_report_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment);
|
||||||
|
|
||||||
|
// Take snapshot on SnapshotDB::singleton(). If taking snapshot fails, report "message", and present a "Continue" or "Abort" buttons to respond.
|
||||||
|
// Return true on success and on "Continue" to continue with the process (for example installation of presets).
|
||||||
|
bool take_config_snapshot_cancel_on_error(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment, const std::string &message);
|
||||||
|
|
||||||
} // namespace Config
|
} // namespace Config
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -59,7 +59,11 @@ bool Bundle::load(fs::path source_path, bool ais_in_resources, bool ais_prusa_bu
|
|||||||
this->is_prusa_bundle = ais_prusa_bundle;
|
this->is_prusa_bundle = ais_prusa_bundle;
|
||||||
|
|
||||||
std::string path_string = source_path.string();
|
std::string path_string = source_path.string();
|
||||||
size_t presets_loaded = preset_bundle->load_configbundle(path_string, PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
// Throw when parsing invalid configuration. Only valid configuration is supposed to be provided over the air.
|
||||||
|
auto [config_substitutions, presets_loaded] = preset_bundle->load_configbundle(
|
||||||
|
path_string, PresetBundle::LoadConfigBundleAttribute::LoadSystem, ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
|
// No substitutions shall be reported when loading a system config bundle, no substitutions are allowed.
|
||||||
|
assert(config_substitutions.empty());
|
||||||
auto first_vendor = preset_bundle->vendors.begin();
|
auto first_vendor = preset_bundle->vendors.begin();
|
||||||
if (first_vendor == preset_bundle->vendors.end()) {
|
if (first_vendor == preset_bundle->vendors.end()) {
|
||||||
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No vendor information defined, cannot install.") % path_string;
|
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: No vendor information defined, cannot install.") % path_string;
|
||||||
@ -2358,7 +2362,7 @@ bool ConfigWizard::priv::check_and_install_missing_materials(Technology technolo
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
|
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater)
|
||||||
{
|
{
|
||||||
const auto enabled_vendors = appconfig_new.vendors();
|
const auto enabled_vendors = appconfig_new.vendors();
|
||||||
|
|
||||||
@ -2407,14 +2411,14 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (snapshot) {
|
if (snapshot && ! take_config_snapshot_cancel_on_error(*app_config, snapshot_reason, "", _u8L("Continue with applying configuration changes?")))
|
||||||
SnapshotDB::singleton().take_snapshot(*app_config, snapshot_reason);
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (install_bundles.size() > 0) {
|
if (install_bundles.size() > 0) {
|
||||||
// Install bundles from resources.
|
// Install bundles from resources.
|
||||||
// Don't create snapshot - we've already done that above if applicable.
|
// Don't create snapshot - we've already done that above if applicable.
|
||||||
updater->install_bundles_rsrc(std::move(install_bundles), false);
|
if (! updater->install_bundles_rsrc(std::move(install_bundles), false))
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
|
BOOST_LOG_TRIVIAL(info) << "No bundles need to be installed from resources";
|
||||||
}
|
}
|
||||||
@ -2490,7 +2494,11 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
preset_bundle->load_presets(*app_config, preferred_model);
|
// Reloading the configs after some modifications were done to PrusaSlicer.ini.
|
||||||
|
// Just perform the substitutions silently, as the substitutions were already presented to the user on application start-up
|
||||||
|
// and the Wizard shall not create any new values that would require substitution.
|
||||||
|
// Throw on substitutions in system profiles, as the system profiles provided over the air should be compatible with this PrusaSlicer version.
|
||||||
|
preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem, preferred_model);
|
||||||
|
|
||||||
if (page_custom->custom_wanted()) {
|
if (page_custom->custom_wanted()) {
|
||||||
page_firmware->apply_custom_config(*custom_config);
|
page_firmware->apply_custom_config(*custom_config);
|
||||||
@ -2504,6 +2512,8 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||||||
|
|
||||||
// Update the selections from the compatibilty.
|
// Update the selections from the compatibilty.
|
||||||
preset_bundle->export_selections(*app_config);
|
preset_bundle->export_selections(*app_config);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2762,7 +2772,8 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page)
|
|||||||
p->set_start_page(start_page);
|
p->set_start_page(start_page);
|
||||||
|
|
||||||
if (ShowModal() == wxID_OK) {
|
if (ShowModal() == wxID_OK) {
|
||||||
p->apply_config(app.app_config, app.preset_bundle, app.preset_updater);
|
if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater))
|
||||||
|
return false;
|
||||||
app.app_config->set_legacy_datadir(false);
|
app.app_config->set_legacy_datadir(false);
|
||||||
app.update_mode();
|
app.update_mode();
|
||||||
app.obj_manipul()->update_ui_from_settings();
|
app.obj_manipul()->update_ui_from_settings();
|
||||||
|
@ -622,7 +622,7 @@ struct ConfigWizard::priv
|
|||||||
|
|
||||||
bool on_bnt_finish();
|
bool on_bnt_finish();
|
||||||
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
|
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
|
||||||
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
|
||||||
// #ys_FIXME_alise
|
// #ys_FIXME_alise
|
||||||
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
|
||||||
|
|
||||||
|
@ -306,9 +306,11 @@ wxWindow* BitmapChoiceRenderer::CreateEditorCtrl(wxWindow* parent, wxRect labelR
|
|||||||
// to avoid event propagation to other sidebar items
|
// to avoid event propagation to other sidebar items
|
||||||
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
c_editor->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent& evt) {
|
||||||
evt.StopPropagation();
|
evt.StopPropagation();
|
||||||
|
#ifdef __linux__
|
||||||
// FinishEditing grabs new selection and triggers config update. We better call
|
// FinishEditing grabs new selection and triggers config update. We better call
|
||||||
// it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
|
// it explicitly, automatic update on KILL_FOCUS didn't work on Linux.
|
||||||
this->FinishEditing();
|
this->FinishEditing();
|
||||||
|
#endif
|
||||||
});
|
});
|
||||||
|
|
||||||
return c_editor;
|
return c_editor;
|
||||||
|
@ -1185,6 +1185,10 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
|||||||
}
|
}
|
||||||
case coEnum: {
|
case coEnum: {
|
||||||
int val = boost::any_cast<int>(value);
|
int val = boost::any_cast<int>(value);
|
||||||
|
if (m_opt_id.compare("host_type") == 0 && val != 0 &&
|
||||||
|
m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType
|
||||||
|
val--;
|
||||||
|
|
||||||
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "solid_fill_pattern"
|
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "solid_fill_pattern"
|
||||||
|| m_opt_id == "fill_pattern" || m_opt_id == "support_material_interface_pattern" || m_opt_id == "brim_ears_pattern")
|
|| m_opt_id == "fill_pattern" || m_opt_id == "support_material_interface_pattern" || m_opt_id == "brim_ears_pattern")
|
||||||
val = idx_from_enum_value<InfillPattern>(val);
|
val = idx_from_enum_value<InfillPattern>(val);
|
||||||
@ -1223,6 +1227,8 @@ void Choice::set_value(const boost::any& value, bool change_event)
|
|||||||
val = idx_from_enum_value<WipeAlgo>(val);
|
val = idx_from_enum_value<WipeAlgo>(val);
|
||||||
else if (m_opt_id.compare("output_format") == 0)
|
else if (m_opt_id.compare("output_format") == 0)
|
||||||
val = idx_from_enum_value<OutputFormat>(val);
|
val = idx_from_enum_value<OutputFormat>(val);
|
||||||
|
else if (m_opt_id.compare("config_compatibility") == 0)
|
||||||
|
val = idx_from_enum_value<ForwardCompatibilitySubstitutionRule>(val);
|
||||||
field->SetSelection(val);
|
field->SetSelection(val);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1279,7 +1285,7 @@ void Choice::set_values(const wxArrayString &values)
|
|||||||
auto ww = dynamic_cast<choice_ctrl*>(window);
|
auto ww = dynamic_cast<choice_ctrl*>(window);
|
||||||
auto value = ww->GetValue();
|
auto value = ww->GetValue();
|
||||||
ww->Clear();
|
ww->Clear();
|
||||||
ww->Append("");
|
// ww->Append("");
|
||||||
for (const auto &el : values)
|
for (const auto &el : values)
|
||||||
ww->Append(el);
|
ww->Append(el);
|
||||||
ww->SetValue(value);
|
ww->SetValue(value);
|
||||||
@ -1302,6 +1308,9 @@ boost::any& Choice::get_value()
|
|||||||
if (m_opt.type == coEnum)
|
if (m_opt.type == coEnum)
|
||||||
{
|
{
|
||||||
int ret_enum = field->GetSelection();
|
int ret_enum = field->GetSelection();
|
||||||
|
if (m_opt_id.compare("host_type") == 0 &&
|
||||||
|
m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType
|
||||||
|
ret_enum++;
|
||||||
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "solid_fill_pattern"
|
if (m_opt_id == "top_fill_pattern" || m_opt_id == "bottom_fill_pattern" || m_opt_id == "solid_fill_pattern"
|
||||||
|| m_opt_id == "support_material_interface_pattern" || m_opt_id == "fill_pattern" || m_opt_id == "brim_ears_pattern")
|
|| m_opt_id == "support_material_interface_pattern" || m_opt_id == "fill_pattern" || m_opt_id == "brim_ears_pattern")
|
||||||
convert_to_enum_value<InfillPattern>(ret_enum);
|
convert_to_enum_value<InfillPattern>(ret_enum);
|
||||||
@ -1340,6 +1349,8 @@ boost::any& Choice::get_value()
|
|||||||
convert_to_enum_value<WipeAlgo>(ret_enum);
|
convert_to_enum_value<WipeAlgo>(ret_enum);
|
||||||
else if (m_opt_id.compare("output_format") == 0)
|
else if (m_opt_id.compare("output_format") == 0)
|
||||||
convert_to_enum_value<OutputFormat>(ret_enum);
|
convert_to_enum_value<OutputFormat>(ret_enum);
|
||||||
|
else if(m_opt_id.compare("config_compatibility") == 0)
|
||||||
|
convert_to_enum_value<ForwardCompatibilitySubstitutionRule>(ret_enum);
|
||||||
}
|
}
|
||||||
else if (m_opt.gui_type == "f_enum_open") {
|
else if (m_opt.gui_type == "f_enum_open") {
|
||||||
const int ret_enum = field->GetSelection();
|
const int ret_enum = field->GetSelection();
|
||||||
|
@ -65,6 +65,8 @@ enum {
|
|||||||
USB_PID_MMU_APP = 4,
|
USB_PID_MMU_APP = 4,
|
||||||
USB_PID_CW1_BOOT = 7,
|
USB_PID_CW1_BOOT = 7,
|
||||||
USB_PID_CW1_APP = 8,
|
USB_PID_CW1_APP = 8,
|
||||||
|
USB_PID_CW1S_BOOT = 14,
|
||||||
|
USB_PID_CW1S_APP = 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This enum discriminates the kind of information in EVT_AVRDUDE,
|
// This enum discriminates the kind of information in EVT_AVRDUDE,
|
||||||
@ -308,7 +310,7 @@ void FirmwareDialog::priv::update_flash_enabled()
|
|||||||
void FirmwareDialog::priv::load_hex_file(const wxString &path)
|
void FirmwareDialog::priv::load_hex_file(const wxString &path)
|
||||||
{
|
{
|
||||||
hex_file = HexFile(path.wx_str());
|
hex_file = HexFile(path.wx_str());
|
||||||
const bool autodetect = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1;
|
const bool autodetect = hex_file.device == HexFile::DEV_MM_CONTROL || hex_file.device == HexFile::DEV_CW1 || hex_file.device == HexFile::DEV_CW1S;
|
||||||
set_autodetect(autodetect);
|
set_autodetect(autodetect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,6 +638,10 @@ void FirmwareDialog::priv::perform_upload()
|
|||||||
this->prepare_avr109(Avr109Pid(USB_PID_CW1_BOOT, USB_PID_CW1_APP));
|
this->prepare_avr109(Avr109Pid(USB_PID_CW1_BOOT, USB_PID_CW1_APP));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case HexFile::DEV_CW1S:
|
||||||
|
this->prepare_avr109(Avr109Pid(USB_PID_CW1S_BOOT, USB_PID_CW1S_APP));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this->prepare_mk2();
|
this->prepare_mk2();
|
||||||
break;
|
break;
|
||||||
@ -761,11 +767,10 @@ const char* FirmwareDialog::priv::avr109_dev_name(Avr109Pid usb_pid) {
|
|||||||
switch (usb_pid.boot) {
|
switch (usb_pid.boot) {
|
||||||
case USB_PID_MMU_BOOT:
|
case USB_PID_MMU_BOOT:
|
||||||
return "Original Prusa MMU 2.0 Control";
|
return "Original Prusa MMU 2.0 Control";
|
||||||
break;
|
|
||||||
case USB_PID_CW1_BOOT:
|
case USB_PID_CW1_BOOT:
|
||||||
return "Original Prusa CW1";
|
return "Original Prusa CW1";
|
||||||
break;
|
case USB_PID_CW1S_BOOT:
|
||||||
|
return "Original Prusa CW1S";
|
||||||
default: throw Slic3r::RuntimeError((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str());
|
default: throw Slic3r::RuntimeError((boost::format("Invalid avr109 device USB PID: %1%") % usb_pid.boot).str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/any.hpp>
|
#include <boost/any.hpp>
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
@ -18,6 +19,7 @@
|
|||||||
|
|
||||||
#include "AboutDialog.hpp"
|
#include "AboutDialog.hpp"
|
||||||
#include "MsgDialog.hpp"
|
#include "MsgDialog.hpp"
|
||||||
|
#include "format.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
|
|
||||||
@ -273,6 +275,127 @@ void warning_catcher(wxWindow* parent, const wxString& message)
|
|||||||
msg.ShowModal();
|
msg.ShowModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static wxString bold(const wxString& str)
|
||||||
|
{
|
||||||
|
return wxString::Format("<b>%s</b>", str);
|
||||||
|
};
|
||||||
|
|
||||||
|
static wxString bold_string(const wxString& str)
|
||||||
|
{
|
||||||
|
return wxString::Format("<b>\"%s\"</b>", str);
|
||||||
|
};
|
||||||
|
|
||||||
|
static void add_config_substitutions(const ConfigSubstitutions& conf_substitutions, wxString& changes)
|
||||||
|
{
|
||||||
|
changes += "<table>";
|
||||||
|
for (const ConfigSubstitution& conf_substitution : conf_substitutions) {
|
||||||
|
wxString new_val;
|
||||||
|
const ConfigOptionDef* def = conf_substitution.opt_def;
|
||||||
|
if (!def)
|
||||||
|
continue;
|
||||||
|
switch (def->type) {
|
||||||
|
case coEnum:
|
||||||
|
{
|
||||||
|
const std::vector<std::string>& labels = def->enum_labels;
|
||||||
|
const std::vector<std::string>& values = def->enum_values;
|
||||||
|
int val = conf_substitution.new_value->getInt();
|
||||||
|
|
||||||
|
bool is_infill = def->opt_key == "top_fill_pattern" ||
|
||||||
|
def->opt_key == "bottom_fill_pattern" ||
|
||||||
|
def->opt_key == "fill_pattern";
|
||||||
|
|
||||||
|
// Each infill doesn't use all list of infill declared in PrintConfig.hpp.
|
||||||
|
// So we should "convert" val to the correct one
|
||||||
|
if (is_infill) {
|
||||||
|
for (const auto& key_val : *def->enum_keys_map)
|
||||||
|
if ((int)key_val.second == val) {
|
||||||
|
auto it = std::find(values.begin(), values.end(), key_val.first);
|
||||||
|
if (it == values.end())
|
||||||
|
break;
|
||||||
|
auto idx = it - values.begin();
|
||||||
|
new_val = wxString("\"") + values[idx] + "\"" + " (" + from_u8(_utf8(labels[idx])) + ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (new_val.IsEmpty()) {
|
||||||
|
assert(false);
|
||||||
|
new_val = _L("Undefined");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
new_val = wxString("\"") + values[val] + "\"" + " (" + from_u8(_utf8(labels[val])) + ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case coBool:
|
||||||
|
new_val = conf_substitution.new_value->getBool() ? "true" : "false";
|
||||||
|
break;
|
||||||
|
case coBools:
|
||||||
|
if (conf_substitution.new_value->nullable())
|
||||||
|
for (const char v : static_cast<const ConfigOptionBoolsNullable*>(conf_substitution.new_value.get())->values)
|
||||||
|
new_val += std::string(v == ConfigOptionBoolsNullable::nil_value() ? "nil" : v ? "true" : "false") + ", ";
|
||||||
|
else
|
||||||
|
for (const char v : static_cast<const ConfigOptionBools*>(conf_substitution.new_value.get())->values)
|
||||||
|
new_val += std::string(v ? "true" : "false") + ", ";
|
||||||
|
if (! new_val.empty())
|
||||||
|
new_val.erase(new_val.begin() + new_val.size() - 2, new_val.end());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
changes += format_wxstr("<tr><td><b>\"%1%\" (%2%)</b></td><td>: ", def->opt_key, _(def->label)) +
|
||||||
|
format_wxstr(_L("%1% was substituted with %2%"), bold_string(conf_substitution.old_value), bold(new_val)) +
|
||||||
|
"</td></tr>";
|
||||||
|
}
|
||||||
|
changes += "</table>";
|
||||||
|
}
|
||||||
|
|
||||||
|
static wxString substitution_message(const wxString& changes)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
_L("Most likely the configuration was produced by a newer version of PrusaSlicer or by some PrusaSlicer fork.") + " " +
|
||||||
|
_L("The following values were substituted:") + "\n" + changes + "\n\n" +
|
||||||
|
_L("Review the substitutions and adjust them if needed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions)
|
||||||
|
{
|
||||||
|
wxString changes;
|
||||||
|
|
||||||
|
auto preset_type_name = [](Preset::Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case Preset::TYPE_FFF_PRINT: return _L("Print settings");
|
||||||
|
case Preset::TYPE_SLA_PRINT: return _L("SLA print settings");
|
||||||
|
case Preset::TYPE_FFF_FILAMENT: return _L("Filament");
|
||||||
|
case Preset::TYPE_SLA_MATERIAL: return _L("SLA material");
|
||||||
|
case Preset::TYPE_PRINTER: return _L("Printer");
|
||||||
|
case Preset::TYPE_PHYSICAL_PRINTER: return _L("Physical Printer");
|
||||||
|
default: assert(false); return wxString();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const PresetConfigSubstitutions& substitution : presets_config_substitutions) {
|
||||||
|
changes += "\n\n" + format_wxstr("%1% : %2%", preset_type_name(substitution.preset_type), bold_string(substitution.preset_name));
|
||||||
|
if (!substitution.preset_file.empty())
|
||||||
|
changes += format_wxstr(" (%1%)", substitution.preset_file);
|
||||||
|
|
||||||
|
add_config_substitutions(substitution.substitutions, changes);
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoDialog msg(nullptr, _L("Configuration bundle was loaded, however some configuration values were not recognized."), substitution_message(changes));
|
||||||
|
msg.ShowModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_substitutions_info(const ConfigSubstitutions& config_substitutions, const std::string& filename)
|
||||||
|
{
|
||||||
|
wxString changes = "\n";
|
||||||
|
add_config_substitutions(config_substitutions, changes);
|
||||||
|
|
||||||
|
InfoDialog msg(nullptr,
|
||||||
|
format_wxstr(_L("Configuration file \"%1%\" was loaded, however some configuration values were not recognized."), from_u8(filename)),
|
||||||
|
substitution_message(changes));
|
||||||
|
msg.ShowModal();
|
||||||
|
}
|
||||||
|
|
||||||
void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items)
|
void create_combochecklist(wxComboCtrl* comboCtrl, const std::string& text, const std::string& items)
|
||||||
{
|
{
|
||||||
if (comboCtrl == nullptr)
|
if (comboCtrl == nullptr)
|
||||||
|
@ -7,6 +7,7 @@ namespace boost::filesystem { class path; }
|
|||||||
#include <wx/string.h>
|
#include <wx/string.h>
|
||||||
|
|
||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
|
#include "libslic3r/Preset.hpp"
|
||||||
|
|
||||||
class wxWindow;
|
class wxWindow;
|
||||||
class wxMenuBar;
|
class wxMenuBar;
|
||||||
@ -49,6 +50,8 @@ void show_info(wxWindow* parent, const wxString& message, const wxString& title
|
|||||||
void show_info(wxWindow* parent, const char* message, const char* title = nullptr);
|
void show_info(wxWindow* parent, const char* message, const char* title = nullptr);
|
||||||
inline void show_info(wxWindow* parent, const std::string& message,const std::string& title = std::string()) { show_info(parent, message.c_str(), title.c_str()); }
|
inline void show_info(wxWindow* parent, const std::string& message,const std::string& title = std::string()) { show_info(parent, message.c_str(), title.c_str()); }
|
||||||
void warning_catcher(wxWindow* parent, const wxString& message);
|
void warning_catcher(wxWindow* parent, const wxString& message);
|
||||||
|
void show_substitutions_info(const PresetsConfigSubstitutions& presets_config_substitutions);
|
||||||
|
void show_substitutions_info(const ConfigSubstitutions& config_substitutions, const std::string& filename);
|
||||||
|
|
||||||
// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items.
|
// Creates a wxCheckListBoxComboPopup inside the given wxComboCtrl, filled with the given text and items.
|
||||||
// Items data must be separated by '|', and contain the item name to be shown followed by its initial value (0 for false, 1 for true).
|
// Items data must be separated by '|', and contain the item name to be shown followed by its initial value (0 for false, 1 for true).
|
||||||
|
@ -443,7 +443,9 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
|||||||
|
|
||||||
/* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG",
|
/* FT_TEX */ "Texture (*.png, *.svg)|*.png;*.PNG;*.svg;*.SVG",
|
||||||
|
|
||||||
/* FT_PNGZIP */ "Masked SLA files (*.sl1)|*.sl1;*.SL1",
|
/* FT_SL1 */ "Masked SLA files (*.sl1, *.sl1s)|*.sl1;*.SL1;*.sl1s;*.SL1S",
|
||||||
|
// Workaround for OSX file picker, for some reason it always saves with the 1st extension.
|
||||||
|
/* FT_SL1S */ "Masked SLA files (*.sl1s, *.sl1)|*.sl1s;*.SL1S;*.sl1;*.SL1",
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string out = defaults[file_type];
|
std::string out = defaults[file_type];
|
||||||
@ -634,6 +636,9 @@ void GUI_App::post_init()
|
|||||||
this->plater()->load_gcode(wxString::FromUTF8(this->init_params->input_files[0].c_str()));
|
this->plater()->load_gcode(wxString::FromUTF8(this->init_params->input_files[0].c_str()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (! this->init_params->preset_substitutions.empty())
|
||||||
|
show_substitutions_info(this->init_params->preset_substitutions);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Load the cummulative config over the currently active profiles.
|
// Load the cummulative config over the currently active profiles.
|
||||||
//FIXME if multiple configs are loaded, only the last one will have an effect.
|
//FIXME if multiple configs are loaded, only the last one will have an effect.
|
||||||
@ -653,6 +658,24 @@ void GUI_App::post_init()
|
|||||||
if (! this->init_params->extra_config.empty())
|
if (! this->init_params->extra_config.empty())
|
||||||
this->mainframe->load_config(this->init_params->extra_config);
|
this->mainframe->load_config(this->init_params->extra_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The extra CallAfter() is needed because of Mac, where this is the only way
|
||||||
|
// to popup a modal dialog on start without screwing combo boxes.
|
||||||
|
// This is ugly but I honestly found no better way to do it.
|
||||||
|
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
||||||
|
if (this->preset_updater) {
|
||||||
|
this->check_updates(false);
|
||||||
|
CallAfter([this] {
|
||||||
|
this->config_wizard_startup();
|
||||||
|
this->preset_updater->slic3r_update_notify();
|
||||||
|
this->preset_updater->sync(preset_bundle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Sets window property to mainframe so other instances can indentify it.
|
||||||
|
OtherInstanceMessageHandler::init_windows_properties(mainframe, m_instance_hash_int);
|
||||||
|
#endif //WIN32
|
||||||
}
|
}
|
||||||
|
|
||||||
IMPLEMENT_APP(GUI_App)
|
IMPLEMENT_APP(GUI_App)
|
||||||
@ -923,7 +946,10 @@ bool GUI_App::on_init_inner()
|
|||||||
// Suppress the '- default -' presets.
|
// Suppress the '- default -' presets.
|
||||||
preset_bundle->set_default_suppressed(app_config->get("no_defaults") == "1");
|
preset_bundle->set_default_suppressed(app_config->get("no_defaults") == "1");
|
||||||
try {
|
try {
|
||||||
preset_bundle->load_presets(*app_config);
|
// Enable all substitutions (in both user and system profiles), but log the substitutions in user profiles only.
|
||||||
|
// If there are substitutions in system profiles, then a "reconfigure" event shall be triggered, which will force
|
||||||
|
// installation of a compatible system preset, thus nullifying the system preset substitutions.
|
||||||
|
init_params->preset_substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSystemSilent);
|
||||||
} catch (const std::exception &ex) {
|
} catch (const std::exception &ex) {
|
||||||
show_error(nullptr, ex.what());
|
show_error(nullptr, ex.what());
|
||||||
}
|
}
|
||||||
@ -976,7 +1002,6 @@ bool GUI_App::on_init_inner()
|
|||||||
if (! plater_)
|
if (! plater_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
if (app_config->dirty() && app_config->get("autosave") == "1")
|
if (app_config->dirty() && app_config->get("autosave") == "1")
|
||||||
app_config->save();
|
app_config->save();
|
||||||
|
|
||||||
@ -997,33 +1022,6 @@ bool GUI_App::on_init_inner()
|
|||||||
#endif
|
#endif
|
||||||
this->post_init();
|
this->post_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preset updating & Configwizard are done after the above initializations,
|
|
||||||
// and after MainFrame is created & shown.
|
|
||||||
// The extra CallAfter() is needed because of Mac, where this is the only way
|
|
||||||
// to popup a modal dialog on start without screwing combo boxes.
|
|
||||||
// This is ugly but I honestly found no better way to do it.
|
|
||||||
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
|
|
||||||
|
|
||||||
static bool once = true;
|
|
||||||
if (once) {
|
|
||||||
once = false;
|
|
||||||
|
|
||||||
if (preset_updater != nullptr) {
|
|
||||||
check_updates(false);
|
|
||||||
|
|
||||||
CallAfter([this] {
|
|
||||||
config_wizard_startup();
|
|
||||||
preset_updater->slic3r_update_notify();
|
|
||||||
preset_updater->sync(preset_bundle);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
//sets window property to mainframe so other instances can indentify it
|
|
||||||
OtherInstanceMessageHandler::init_windows_properties(mainframe, m_instance_hash_int);
|
|
||||||
#endif //WIN32
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
@ -1819,9 +1817,10 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||||||
child->SetFont(normal_font());
|
child->SetFont(normal_font());
|
||||||
|
|
||||||
if (dlg.ShowModal() == wxID_OK)
|
if (dlg.ShowModal() == wxID_OK)
|
||||||
app_config->set("on_snapshot",
|
if (const Config::Snapshot *snapshot = Config::take_config_snapshot_report_error(
|
||||||
Slic3r::GUI::Config::SnapshotDB::singleton().take_snapshot(
|
*app_config, Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data());
|
||||||
*app_config, Slic3r::GUI::Config::Snapshot::SNAPSHOT_USER, dlg.GetValue().ToUTF8().data()).id);
|
snapshot != nullptr)
|
||||||
|
app_config->set("on_snapshot", snapshot->id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ConfigMenuSnapshots:
|
case ConfigMenuSnapshots:
|
||||||
@ -1832,13 +1831,24 @@ void GUI_App::add_config_menu(wxMenuBar *menu)
|
|||||||
ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot);
|
ConfigSnapshotDialog dlg(Slic3r::GUI::Config::SnapshotDB::singleton(), on_snapshot);
|
||||||
dlg.ShowModal();
|
dlg.ShowModal();
|
||||||
if (!dlg.snapshot_to_activate().empty()) {
|
if (!dlg.snapshot_to_activate().empty()) {
|
||||||
if (! Config::SnapshotDB::singleton().is_on_snapshot(*app_config))
|
if (! Config::SnapshotDB::singleton().is_on_snapshot(*app_config) &&
|
||||||
Config::SnapshotDB::singleton().take_snapshot(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK);
|
! Config::take_config_snapshot_cancel_on_error(*app_config, Config::Snapshot::SNAPSHOT_BEFORE_ROLLBACK, "",
|
||||||
|
GUI::format(_L("Continue to activate a configuration snapshot %1%?"), dlg.snapshot_to_activate())))
|
||||||
|
break;
|
||||||
try {
|
try {
|
||||||
app_config->set("on_snapshot", Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id);
|
app_config->set("on_snapshot", Config::SnapshotDB::singleton().restore_snapshot(dlg.snapshot_to_activate(), *app_config).id);
|
||||||
preset_bundle->load_presets(*app_config);
|
// Enable substitutions, log both user and system substitutions. There should not be any substitutions performed when loading system
|
||||||
|
// presets because compatibility of profiles shall be verified using the min_slic3r_version keys in config index, but users
|
||||||
|
// are known to be creative and mess with the config files in various ways.
|
||||||
|
if (PresetsConfigSubstitutions all_substitutions = preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::Enable);
|
||||||
|
! all_substitutions.empty())
|
||||||
|
show_substitutions_info(all_substitutions);
|
||||||
|
|
||||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||||
load_current_presets();
|
load_current_presets();
|
||||||
|
|
||||||
|
// update config wizard in respect to the new config
|
||||||
|
update_wizard_from_config();
|
||||||
} catch (std::exception &ex) {
|
} catch (std::exception &ex) {
|
||||||
GUI::show_error(nullptr, _L("Failed to activate configuration snapshot.") + "\n" + into_u8(ex.what()));
|
GUI::show_error(nullptr, _L("Failed to activate configuration snapshot.") + "\n" + into_u8(ex.what()));
|
||||||
}
|
}
|
||||||
@ -2015,6 +2025,17 @@ void GUI_App::load_current_presets(bool check_printer_presets_ /*= true*/)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GUI_App::update_wizard_from_config()
|
||||||
|
{
|
||||||
|
if (!m_wizard)
|
||||||
|
return;
|
||||||
|
// If ConfigWizard was created before changing of the configuration,
|
||||||
|
// we have to destroy it to have possibility to create it again in respect to the new config's parameters
|
||||||
|
m_wizard->Reparent(nullptr);
|
||||||
|
m_wizard->Destroy();
|
||||||
|
m_wizard = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
bool GUI_App::OnExceptionInMainLoop()
|
bool GUI_App::OnExceptionInMainLoop()
|
||||||
{
|
{
|
||||||
generic_exception_handle();
|
generic_exception_handle();
|
||||||
@ -2175,7 +2196,13 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
|
|||||||
{
|
{
|
||||||
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
|
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
|
||||||
|
|
||||||
|
if (reason == ConfigWizard::RR_USER)
|
||||||
|
if (PresetUpdater::UpdateResult result = preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD);
|
||||||
|
result == PresetUpdater::R_ALL_CANCELED)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (! m_wizard) {
|
if (! m_wizard) {
|
||||||
|
wxBusyCursor wait;
|
||||||
m_wizard = new ConfigWizard(mainframe);
|
m_wizard = new ConfigWizard(mainframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2334,7 +2361,7 @@ void GUI_App::check_updates(const bool verbose)
|
|||||||
{
|
{
|
||||||
PresetUpdater::UpdateResult updater_result;
|
PresetUpdater::UpdateResult updater_result;
|
||||||
try {
|
try {
|
||||||
updater_result = preset_updater->config_update(app_config->orig_version(), verbose);
|
updater_result = preset_updater->config_update(app_config->orig_version(), verbose ? PresetUpdater::UpdateParams::SHOW_TEXT_BOX : PresetUpdater::UpdateParams::SHOW_NOTIFICATION);
|
||||||
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
|
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
|
||||||
mainframe->Close();
|
mainframe->Close();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,9 @@ enum FileType
|
|||||||
|
|
||||||
FT_TEX,
|
FT_TEX,
|
||||||
|
|
||||||
FT_PNGZIP,
|
FT_SL1,
|
||||||
|
// Workaround for OSX file picker, for some reason it always saves with the 1st extension.
|
||||||
|
FT_SL1S,
|
||||||
|
|
||||||
FT_SIZE,
|
FT_SIZE,
|
||||||
};
|
};
|
||||||
@ -226,6 +228,7 @@ public:
|
|||||||
bool check_print_host_queue();
|
bool check_print_host_queue();
|
||||||
bool checked_tab(Tab* tab);
|
bool checked_tab(Tab* tab);
|
||||||
void load_current_presets(bool check_printer_presets = true);
|
void load_current_presets(bool check_printer_presets = true);
|
||||||
|
void update_wizard_from_config();
|
||||||
|
|
||||||
wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
|
wxString current_language_code() const { return m_wxLocale->GetCanonicalName(); }
|
||||||
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
|
// Translate the language code to a code, for which Prusa Research maintains translations. Defaults to "en_US".
|
||||||
@ -250,7 +253,7 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
// Parameters extracted from the command line to be passed to GUI after initialization.
|
// Parameters extracted from the command line to be passed to GUI after initialization.
|
||||||
const GUI_InitParams* init_params { nullptr };
|
GUI_InitParams* init_params { nullptr };
|
||||||
|
|
||||||
AppConfig* app_config{ nullptr };
|
AppConfig* app_config{ nullptr };
|
||||||
PresetBundle* preset_bundle{ nullptr };
|
PresetBundle* preset_bundle{ nullptr };
|
||||||
@ -260,7 +263,7 @@ public:
|
|||||||
std::mutex not_modal_dialog_mutex;
|
std::mutex not_modal_dialog_mutex;
|
||||||
wxDialog* not_modal_dialog = nullptr;
|
wxDialog* not_modal_dialog = nullptr;
|
||||||
|
|
||||||
PresetUpdater* get_preset_updater() { return preset_updater; }
|
PresetUpdater* get_preset_updater() { return preset_updater; }
|
||||||
|
|
||||||
wxNotebook* tab_panel() const ;
|
wxNotebook* tab_panel() const ;
|
||||||
int extruders_cnt() const;
|
int extruders_cnt() const;
|
||||||
|
@ -50,39 +50,8 @@ int GUI_Run(GUI_InitParams ¶ms)
|
|||||||
// gui->autosave = m_config.opt_string("autosave");
|
// gui->autosave = m_config.opt_string("autosave");
|
||||||
GUI::GUI_App::SetInstance(gui);
|
GUI::GUI_App::SetInstance(gui);
|
||||||
gui->init_params = ¶ms;
|
gui->init_params = ¶ms;
|
||||||
/*
|
|
||||||
gui->CallAfter([gui, this, &load_configs, params.start_as_gcodeviewer] {
|
|
||||||
if (!gui->initialized()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.start_as_gcodeviewer) {
|
return wxEntry(params.argc, params.argv);
|
||||||
if (!m_input_files.empty())
|
|
||||||
gui->plater()->load_gcode(wxString::FromUTF8(m_input_files[0].c_str()));
|
|
||||||
} else {
|
|
||||||
#if 0
|
|
||||||
// Load the cummulative config over the currently active profiles.
|
|
||||||
//FIXME if multiple configs are loaded, only the last one will have an effect.
|
|
||||||
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
|
|
||||||
// As of now only the full configs are supported here.
|
|
||||||
if (!m_print_config.empty())
|
|
||||||
gui->mainframe->load_config(m_print_config);
|
|
||||||
#endif
|
|
||||||
if (!load_configs.empty())
|
|
||||||
// Load the last config to give it a name at the UI. The name of the preset may be later
|
|
||||||
// changed by loading an AMF or 3MF.
|
|
||||||
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
|
|
||||||
gui->mainframe->load_config_file(load_configs.back());
|
|
||||||
// If loading a 3MF file, the config is loaded from the last one.
|
|
||||||
if (!m_input_files.empty())
|
|
||||||
gui->plater()->load_files(m_input_files, true, true);
|
|
||||||
if (!m_extra_config.empty())
|
|
||||||
gui->mainframe->load_config(m_extra_config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
int result = wxEntry(params.argc, params.argv);
|
|
||||||
return result;
|
|
||||||
} catch (const Slic3r::Exception &ex) {
|
} catch (const Slic3r::Exception &ex) {
|
||||||
boost::nowide::cerr << ex.what() << std::endl;
|
boost::nowide::cerr << ex.what() << std::endl;
|
||||||
wxMessageBox(boost::nowide::widen(ex.what()), _L(SLIC3R_APP_NAME " GUI initialization failed"), wxICON_STOP);
|
wxMessageBox(boost::nowide::widen(ex.what()), _L(SLIC3R_APP_NAME " GUI initialization failed"), wxICON_STOP);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#ifndef slic3r_GUI_Init_hpp_
|
#ifndef slic3r_GUI_Init_hpp_
|
||||||
#define slic3r_GUI_Init_hpp_
|
#define slic3r_GUI_Init_hpp_
|
||||||
|
|
||||||
|
#include <libslic3r/Preset.hpp>
|
||||||
#include <libslic3r/PrintConfig.hpp>
|
#include <libslic3r/PrintConfig.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@ -12,6 +13,9 @@ struct GUI_InitParams
|
|||||||
int argc;
|
int argc;
|
||||||
char **argv;
|
char **argv;
|
||||||
|
|
||||||
|
// Substitutions of unknown configuration values done during loading of user presets.
|
||||||
|
PresetsConfigSubstitutions preset_substitutions;
|
||||||
|
|
||||||
std::vector<std::string> load_configs;
|
std::vector<std::string> load_configs;
|
||||||
DynamicPrintConfig extra_config;
|
DynamicPrintConfig extra_config;
|
||||||
std::vector<std::string> input_files;
|
std::vector<std::string> input_files;
|
||||||
|
@ -33,7 +33,7 @@ public:
|
|||||||
|
|
||||||
m_filepicker = new wxFilePickerCtrl(this, wxID_ANY,
|
m_filepicker = new wxFilePickerCtrl(this, wxID_ANY,
|
||||||
from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")),
|
from_u8(wxGetApp().app_config->get_last_dir()), _(L("Choose SLA archive:")),
|
||||||
"SL1 archive files (*.sl1, *.zip)|*.sl1;*.SL1;*.zip;*.ZIP",
|
"SL1 / SL1S archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP",
|
||||||
wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
|
|
||||||
szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER);
|
szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER);
|
||||||
@ -114,11 +114,12 @@ public:
|
|||||||
|
|
||||||
Sel sel = Sel::modelAndProfile;
|
Sel sel = Sel::modelAndProfile;
|
||||||
|
|
||||||
TriangleMesh mesh;
|
TriangleMesh mesh;
|
||||||
DynamicPrintConfig profile;
|
DynamicPrintConfig profile;
|
||||||
wxString path;
|
wxString path;
|
||||||
Vec2i32 win = {2, 2};
|
Vec2i32 win = {2, 2};
|
||||||
std::string err;
|
std::string err;
|
||||||
|
ConfigSubstitutions config_substitutions;
|
||||||
|
|
||||||
priv(Plater *plt): plater{plt} {}
|
priv(Plater *plt): plater{plt} {}
|
||||||
};
|
};
|
||||||
@ -142,13 +143,13 @@ void SLAImportJob::process()
|
|||||||
try {
|
try {
|
||||||
switch (p->sel) {
|
switch (p->sel) {
|
||||||
case Sel::modelAndProfile:
|
case Sel::modelAndProfile:
|
||||||
import_sla_archive(path, p->win, p->mesh, p->profile, progr);
|
p->config_substitutions = import_sla_archive(path, p->win, p->mesh, p->profile, progr);
|
||||||
break;
|
break;
|
||||||
case Sel::modelOnly:
|
case Sel::modelOnly:
|
||||||
import_sla_archive(path, p->win, p->mesh, progr);
|
p->config_substitutions = import_sla_archive(path, p->win, p->mesh, progr);
|
||||||
break;
|
break;
|
||||||
case Sel::profileOnly:
|
case Sel::profileOnly:
|
||||||
import_sla_archive(path, p->profile);
|
p->config_substitutions = import_sla_archive(path, p->profile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,6 +182,7 @@ void SLAImportJob::prepare()
|
|||||||
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : path.ToUTF8();
|
p->path = !nm.Exists(wxFILE_EXISTS_REGULAR) ? "" : path.ToUTF8();
|
||||||
p->sel = dlg.get_selection();
|
p->sel = dlg.get_selection();
|
||||||
p->win = dlg.get_marchsq_windowsize();
|
p->win = dlg.get_marchsq_windowsize();
|
||||||
|
p->config_substitutions.clear();
|
||||||
} else {
|
} else {
|
||||||
p->path = "";
|
p->path = "";
|
||||||
}
|
}
|
||||||
@ -224,7 +226,10 @@ void SLAImportJob::finalize()
|
|||||||
p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name, is_centered);
|
p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name, is_centered);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! p->config_substitutions.empty())
|
||||||
|
show_substitutions_info(p->config_substitutions, p->path.ToUTF8().data());
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
}} // namespace Slic3r::GUI
|
||||||
|
@ -1268,7 +1268,7 @@ void MainFrame::init_menubar_as_editor()
|
|||||||
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(true); }, "import_plater", nullptr,
|
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(true); }, "import_plater", nullptr,
|
||||||
[this](){return m_plater != nullptr; }, this);
|
[this](){return m_plater != nullptr; }, this);
|
||||||
|
|
||||||
append_menu_item(import_menu, wxID_ANY, _L("Import SL1 archive") + dots, _L("Load an SL1 archive"),
|
append_menu_item(import_menu, wxID_ANY, _L("Import SL1 / SL1S archive") + dots, _L("Load an SL1 / Sl1S archive"),
|
||||||
[this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr,
|
[this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr,
|
||||||
[this](){return m_plater != nullptr; }, this);
|
[this](){return m_plater != nullptr; }, this);
|
||||||
|
|
||||||
@ -1625,6 +1625,7 @@ void MainFrame::update_menubar()
|
|||||||
m_changeable_menu_items[miPrinterTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "printer" : "sla_printer"));
|
m_changeable_menu_items[miPrinterTab] ->SetBitmap(create_scaled_bitmap(is_fff ? "printer" : "sla_printer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
|
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
|
||||||
void MainFrame::quick_slice(const int qs)
|
void MainFrame::quick_slice(const int qs)
|
||||||
{
|
{
|
||||||
@ -1747,6 +1748,7 @@ void MainFrame::quick_slice(const int qs)
|
|||||||
// };
|
// };
|
||||||
// Slic3r::GUI::catch_error(this, []() { if (m_progress_dialog) m_progress_dialog->Destroy(); });
|
// Slic3r::GUI::catch_error(this, []() { if (m_progress_dialog) m_progress_dialog->Destroy(); });
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void MainFrame::reslice_now()
|
void MainFrame::reslice_now()
|
||||||
{
|
{
|
||||||
@ -1829,7 +1831,9 @@ void MainFrame::load_config_file()
|
|||||||
bool MainFrame::load_config_file(const std::string &path)
|
bool MainFrame::load_config_file(const std::string &path)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
wxGetApp().preset_bundle->load_config_file(path);
|
ConfigSubstitutions config_substitutions = wxGetApp().preset_bundle->load_config_file(path, ForwardCompatibilitySubstitutionRule::Enable);
|
||||||
|
if (!config_substitutions.empty())
|
||||||
|
show_substitutions_info(config_substitutions, path);
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
show_error(this, ex.what());
|
show_error(this, ex.what());
|
||||||
return false;
|
return false;
|
||||||
@ -1885,14 +1889,20 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
|
|||||||
|
|
||||||
wxGetApp().app_config->update_config_dir(get_dir_name(file));
|
wxGetApp().app_config->update_config_dir(get_dir_name(file));
|
||||||
|
|
||||||
auto presets_imported = 0;
|
size_t presets_imported = 0;
|
||||||
|
PresetsConfigSubstitutions config_substitutions;
|
||||||
try {
|
try {
|
||||||
presets_imported = wxGetApp().preset_bundle->load_configbundle(file.ToUTF8().data());
|
// Report all substitutions.
|
||||||
|
std::tie(config_substitutions, presets_imported) = wxGetApp().preset_bundle->load_configbundle(
|
||||||
|
file.ToUTF8().data(), PresetBundle::LoadConfigBundleAttribute::SaveImported, ForwardCompatibilitySubstitutionRule::Enable);
|
||||||
} catch (const std::exception &ex) {
|
} catch (const std::exception &ex) {
|
||||||
show_error(this, ex.what());
|
show_error(this, ex.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! config_substitutions.empty())
|
||||||
|
show_substitutions_info(config_substitutions);
|
||||||
|
|
||||||
// Load the currently selected preset into the GUI, update the preset selection box.
|
// Load the currently selected preset into the GUI, update the preset selection box.
|
||||||
wxGetApp().load_current_presets();
|
wxGetApp().load_current_presets();
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ public:
|
|||||||
bool is_last_input_file() const { return !m_qs_last_input_file.IsEmpty(); }
|
bool is_last_input_file() const { return !m_qs_last_input_file.IsEmpty(); }
|
||||||
ESettingsLayout get_layout() const { return m_layout; }
|
ESettingsLayout get_layout() const { return m_layout; }
|
||||||
|
|
||||||
void quick_slice(const int qs = qsUndef);
|
// void quick_slice(const int qs = qsUndef);
|
||||||
void reslice_now();
|
void reslice_now();
|
||||||
void repair_stl();
|
void repair_stl();
|
||||||
void export_config(bool to_prusa = false);
|
void export_config(bool to_prusa = false);
|
||||||
|
@ -58,7 +58,7 @@ MsgDialog::MsgDialog(wxWindow *parent, const wxString &title, const wxString &he
|
|||||||
|
|
||||||
logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
|
logo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap);
|
||||||
|
|
||||||
topsizer->Add(logo, 0, wxALL, BORDER);
|
topsizer->Add(logo, 0, /*wxALL*/wxTOP | wxBOTTOM | wxLEFT, BORDER);
|
||||||
topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER);
|
topsizer->Add(rightsizer, 1, wxALL | wxEXPAND, BORDER);
|
||||||
|
|
||||||
SetSizerAndFit(topsizer);
|
SetSizerAndFit(topsizer);
|
||||||
@ -107,5 +107,57 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_
|
|||||||
Fit();
|
Fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// InfoDialog
|
||||||
|
|
||||||
|
InfoDialog::InfoDialog(wxWindow* parent, const wxString &title, const wxString& msg)
|
||||||
|
: MsgDialog(parent, wxString::Format(_L("%s information"), SLIC3R_APP_NAME), title)
|
||||||
|
, msg(msg)
|
||||||
|
{
|
||||||
|
this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
|
|
||||||
|
// Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
|
||||||
|
wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
|
||||||
|
{
|
||||||
|
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
||||||
|
wxFont monospace = wxGetApp().code_font();
|
||||||
|
wxColour text_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
|
||||||
|
wxColour bgr_clr = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
|
||||||
|
auto text_clr_str = wxString::Format(wxT("#%02X%02X%02X"), text_clr.Red(), text_clr.Green(), text_clr.Blue());
|
||||||
|
auto bgr_clr_str = wxString::Format(wxT("#%02X%02X%02X"), bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue());
|
||||||
|
const int font_size = font.GetPointSize() - 1;
|
||||||
|
int size[] = { font_size, font_size, font_size, font_size, font_size, font_size, font_size };
|
||||||
|
html->SetFonts(font.GetFaceName(), monospace.GetFaceName(), size);
|
||||||
|
html->SetBorders(2);
|
||||||
|
|
||||||
|
// calculate html page size from text
|
||||||
|
int lines = msg.Freq('\n');
|
||||||
|
|
||||||
|
if (msg.Contains("<tr>")) {
|
||||||
|
int pos = 0;
|
||||||
|
while (pos < (int)msg.Len() && pos != wxNOT_FOUND) {
|
||||||
|
pos = msg.find("<tr>", pos + 1);
|
||||||
|
lines+=2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int page_height = std::min((font.GetPixelSize().y + 1) * lines, 68 * wxGetApp().em_unit());
|
||||||
|
wxSize page_size(68 * wxGetApp().em_unit(), page_height);
|
||||||
|
|
||||||
|
html->SetMinSize(page_size);
|
||||||
|
|
||||||
|
std::string msg_escaped = xml_escape(msg.ToUTF8().data(), true);
|
||||||
|
boost::replace_all(msg_escaped, "\r\n", "<br>");
|
||||||
|
boost::replace_all(msg_escaped, "\n", "<br>");
|
||||||
|
html->SetPage("<html><body bgcolor=\"" + bgr_clr_str + "\"><font color=\"" + text_clr_str + "\">" + wxString::FromUTF8(msg_escaped.data()) + "</font></body></html>");
|
||||||
|
content_sizer->Add(html, 1, wxEXPAND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set info bitmap
|
||||||
|
logo->SetBitmap(create_scaled_bitmap("info", this, 84));
|
||||||
|
|
||||||
|
Fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,22 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Generic info dialog, used for displaying exceptions
|
||||||
|
class InfoDialog : public MsgDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InfoDialog(wxWindow *parent, const wxString &title, const wxString &msg);
|
||||||
|
InfoDialog(InfoDialog&&) = delete;
|
||||||
|
InfoDialog(const InfoDialog&) = delete;
|
||||||
|
InfoDialog&operator=(InfoDialog&&) = delete;
|
||||||
|
InfoDialog&operator=(const InfoDialog&) = delete;
|
||||||
|
virtual ~InfoDialog() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
wxString msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "libslic3r/AppConfig.hpp"
|
#include "libslic3r/AppConfig.hpp"
|
||||||
|
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
|
#include "libslic3r/Config.hpp"
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
@ -36,7 +37,25 @@ wxDEFINE_EVENT(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED, EjectDriveNotificationClicke
|
|||||||
wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
|
wxDEFINE_EVENT(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, ExportGcodeNotificationClickedEvent);
|
||||||
wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent);
|
wxDEFINE_EVENT(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, PresetUpdateAvailableClickedEvent);
|
||||||
|
|
||||||
namespace Notifications_Internal{
|
const NotificationManager::NotificationData NotificationManager::basic_notifications[] = {
|
||||||
|
// {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")},
|
||||||
|
// {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") },
|
||||||
|
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
|
||||||
|
// {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") },
|
||||||
|
// {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") },
|
||||||
|
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."), [](wxEvtHandler* evnthndlr){
|
||||||
|
if (evnthndlr != nullptr) wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED)); return true; }},
|
||||||
|
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr){
|
||||||
|
wxLaunchDefaultBrowser("https://github.com/" SLIC3R_GITHUB "/releases"); return true; }},
|
||||||
|
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
|
||||||
|
_u8L("You have just added a G-code for color change, but its value is empty.\n"
|
||||||
|
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
||||||
|
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
||||||
|
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
|
||||||
|
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace {
|
||||||
ImFont* add_default_font(float pixel_size)
|
ImFont* add_default_font(float pixel_size)
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
@ -48,7 +67,7 @@ namespace Notifications_Internal{
|
|||||||
return font;
|
return font;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
|
inline void push_style_color(ImGuiCol idx, const ImVec4& col, bool fading_out, float current_fade_opacity)
|
||||||
{
|
{
|
||||||
if (fading_out)
|
if (fading_out)
|
||||||
ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
|
ImGui::PushStyleColor(idx, ImVec4(col.x, col.y, col.z, col.w * current_fade_opacity));
|
||||||
@ -138,8 +157,8 @@ NotificationManager::PopNotification::PopNotification(const NotificationData &n,
|
|||||||
, m_last_remaining_time (n.duration)
|
, m_last_remaining_time (n.duration)
|
||||||
, m_counting_down (n.duration != 0)
|
, m_counting_down (n.duration != 0)
|
||||||
, m_text1 (n.text1)
|
, m_text1 (n.text1)
|
||||||
, m_hypertext (n.hypertext)
|
, m_hypertext (n.hypertext)
|
||||||
, m_text2 (n.text2)
|
, m_text2 (n.text2)
|
||||||
, m_evt_handler (evt_handler)
|
, m_evt_handler (evt_handler)
|
||||||
, m_notification_start (GLCanvas3D::timestamp_now())
|
, m_notification_start (GLCanvas3D::timestamp_now())
|
||||||
{
|
{
|
||||||
@ -187,26 +206,26 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
|
|||||||
// color change based on fading out
|
// color change based on fading out
|
||||||
bool fading_pop = false;
|
bool fading_pop = false;
|
||||||
if (m_fading_out) {
|
if (m_fading_out) {
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity);
|
||||||
fading_pop = true;
|
fading_pop = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// background color
|
// background color
|
||||||
if (m_is_gray) {
|
if (m_is_gray) {
|
||||||
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
|
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||||
}
|
}
|
||||||
else if (m_data.level == NotificationLevel::ErrorNotification) {
|
else if (m_data.level == NotificationLevel::ErrorNotification) {
|
||||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||||
backcolor.x += 0.3f;
|
backcolor.x += 0.3f;
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||||
}
|
}
|
||||||
else if (m_data.level == NotificationLevel::WarningNotification) {
|
else if (m_data.level == NotificationLevel::WarningNotification) {
|
||||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||||
backcolor.x += 0.3f;
|
backcolor.x += 0.3f;
|
||||||
backcolor.y += 0.15f;
|
backcolor.y += 0.15f;
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
// name of window - probably indentifies window and is shown so last_end add whitespaces according to id
|
// name of window - probably indentifies window and is shown so last_end add whitespaces according to id
|
||||||
@ -304,23 +323,23 @@ NotificationManager::PopNotification::RenderResult NotificationManager::PopNotif
|
|||||||
if (m_fading_out) {
|
if (m_fading_out) {
|
||||||
if (!m_paused)
|
if (!m_paused)
|
||||||
m_current_fade_opacity -= 1.f / ((m_fading_time + 1.f) * 60.f);
|
m_current_fade_opacity -= 1.f / ((m_fading_time + 1.f) * 60.f);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity);
|
||||||
fading_pop = true;
|
fading_pop = true;
|
||||||
}
|
}
|
||||||
// background color
|
// background color
|
||||||
if (m_is_gray) {
|
if (m_is_gray) {
|
||||||
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
|
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||||
} else if (m_data.level == NotificationLevel::ErrorNotification) {
|
} else if (m_data.level == NotificationLevel::ErrorNotification) {
|
||||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||||
backcolor.x += 0.3f;
|
backcolor.x += 0.3f;
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||||
} else if (m_data.level == NotificationLevel::WarningNotification) {
|
} else if (m_data.level == NotificationLevel::WarningNotification) {
|
||||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||||
backcolor.x += 0.3f;
|
backcolor.x += 0.3f;
|
||||||
backcolor.y += 0.15f;
|
backcolor.y += 0.15f;
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
//name of window - probably indentifies window and is shown so last_end add whitespaces according to id
|
//name of window - probably indentifies window and is shown so last_end add whitespaces according to id
|
||||||
@ -414,7 +433,7 @@ void NotificationManager::PopNotification::init()
|
|||||||
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
|
if (ImGui::CalcTextSize(text.substr(last_end).c_str()).x >= m_window_width - m_window_width_offset) {
|
||||||
// more than one line till end
|
// more than one line till end
|
||||||
int next_space = text.find_first_of(' ', last_end);
|
int next_space = text.find_first_of(' ', last_end);
|
||||||
if (next_space > 0) {
|
if (next_space > 0 && next_space < text.length()) {
|
||||||
int next_space_candidate = text.find_first_of(' ', next_space + 1);
|
int next_space_candidate = text.find_first_of(' ', next_space + 1);
|
||||||
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
|
while (next_space_candidate > 0 && ImGui::CalcTextSize(text.substr(last_end, next_space_candidate - last_end).c_str()).x < m_window_width - m_window_width_offset) {
|
||||||
next_space = next_space_candidate;
|
next_space = next_space_candidate;
|
||||||
@ -524,7 +543,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
|||||||
orange_color.y += 0.2f;
|
orange_color.y += 0.2f;
|
||||||
|
|
||||||
//text
|
//text
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_Text, orange_color, m_fading_out, m_current_fade_opacity);
|
||||||
ImGui::SetCursorPosX(text_x);
|
ImGui::SetCursorPosX(text_x);
|
||||||
ImGui::SetCursorPosY(text_y);
|
ImGui::SetCursorPosY(text_y);
|
||||||
imgui.text(text.c_str());
|
imgui.text(text.c_str());
|
||||||
@ -547,8 +566,8 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
|
|||||||
orange_color.w = 0.8f;
|
orange_color.w = 0.8f;
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
|
|
||||||
|
|
||||||
@ -647,9 +666,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
|
|||||||
{
|
{
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||||
|
|
||||||
|
|
||||||
//button - if part if treggered
|
//button - if part if treggered
|
||||||
@ -852,8 +871,9 @@ void NotificationManager::ExportFinishedNotification::render_text(ImGuiWrapper&
|
|||||||
for (size_t i = 0; i < m_lines_count; i++) {
|
for (size_t i = 0; i < m_lines_count; i++) {
|
||||||
if (m_text1.size() >= m_endlines[i]) {
|
if (m_text1.size() >= m_endlines[i]) {
|
||||||
std::string line = m_text1.substr(last_end, m_endlines[i] - last_end);
|
std::string line = m_text1.substr(last_end, m_endlines[i] - last_end);
|
||||||
if (i < m_lines_count - 1)
|
last_end = m_endlines[i];
|
||||||
last_end = m_endlines[i] + (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
if (m_text1.size() > m_endlines[i])
|
||||||
|
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
|
||||||
ImGui::SetCursorPosX(x_offset);
|
ImGui::SetCursorPosX(x_offset);
|
||||||
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
ImGui::SetCursorPosY(starting_y + i * shift_y);
|
||||||
imgui.text(line.c_str());
|
imgui.text(line.c_str());
|
||||||
@ -881,8 +901,8 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
|
|||||||
orange_color.w = 0.8f;
|
orange_color.w = 0.8f;
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||||
|
|
||||||
std::string button_text;
|
std::string button_text;
|
||||||
@ -937,7 +957,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
|
|||||||
}
|
}
|
||||||
bool NotificationManager::ExportFinishedNotification::on_text_click()
|
bool NotificationManager::ExportFinishedNotification::on_text_click()
|
||||||
{
|
{
|
||||||
Notifications_Internal::open_folder(m_export_dir_path);
|
open_folder(m_export_dir_path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//------ProgressBar----------------
|
//------ProgressBar----------------
|
||||||
@ -980,10 +1000,10 @@ NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
|
|||||||
}
|
}
|
||||||
void NotificationManager::push_notification(const NotificationType type, int timestamp)
|
void NotificationManager::push_notification(const NotificationType type, int timestamp)
|
||||||
{
|
{
|
||||||
auto it = std::find_if(basic_notifications.begin(), basic_notifications.end(),
|
auto it = std::find_if(std::begin(basic_notifications), std::end(basic_notifications),
|
||||||
boost::bind(&NotificationData::type, boost::placeholders::_1) == type);
|
boost::bind(&NotificationData::type, boost::placeholders::_1) == type);
|
||||||
assert(it != basic_notifications.end());
|
assert(it != std::end(basic_notifications));
|
||||||
if (it != basic_notifications.end())
|
if (it != std::end(basic_notifications))
|
||||||
push_notification_data(*it, timestamp);
|
push_notification_data(*it, timestamp);
|
||||||
}
|
}
|
||||||
void NotificationManager::push_notification(const std::string& text, int timestamp)
|
void NotificationManager::push_notification(const std::string& text, int timestamp)
|
||||||
|
@ -167,7 +167,7 @@ private:
|
|||||||
// Callback for hypertext - returns true if notification should close after triggering
|
// Callback for hypertext - returns true if notification should close after triggering
|
||||||
// Usually sends event to UI thread thru wxEvtHandler.
|
// Usually sends event to UI thread thru wxEvtHandler.
|
||||||
// Examples in basic_notifications.
|
// Examples in basic_notifications.
|
||||||
std::function<bool(wxEvtHandler*)> callback { nullptr };
|
std::function<bool(wxEvtHandler*)> callback;
|
||||||
const std::string text2;
|
const std::string text2;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -231,7 +231,7 @@ private:
|
|||||||
//returns top in actual frame
|
//returns top in actual frame
|
||||||
float get_current_top() const { return m_top_y; }
|
float get_current_top() const { return m_top_y; }
|
||||||
const NotificationType get_type() const { return m_data.type; }
|
const NotificationType get_type() const { return m_data.type; }
|
||||||
const NotificationData get_data() const { return m_data; }
|
const NotificationData& get_data() const { return m_data; }
|
||||||
const bool is_gray() const { return m_is_gray; }
|
const bool is_gray() const { return m_is_gray; }
|
||||||
// Call equals one second down
|
// Call equals one second down
|
||||||
void substract_remaining_time(int seconds) { m_remaining_time -= seconds; }
|
void substract_remaining_time(int seconds) { m_remaining_time -= seconds; }
|
||||||
@ -383,10 +383,10 @@ private:
|
|||||||
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); }
|
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); }
|
||||||
void set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) m_progress_complete = true; else m_progress_complete = false; }
|
void set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) m_progress_complete = true; else m_progress_complete = false; }
|
||||||
protected:
|
protected:
|
||||||
virtual void init();
|
void init();
|
||||||
virtual void render_text(ImGuiWrapper& imgui,
|
void render_text(ImGuiWrapper& imgui,
|
||||||
const float win_size_x, const float win_size_y,
|
const float win_size_x, const float win_size_y,
|
||||||
const float win_pos_x, const float win_pos_y);
|
const float win_pos_x, const float win_pos_y) override;
|
||||||
void render_bar(ImGuiWrapper& imgui,
|
void render_bar(ImGuiWrapper& imgui,
|
||||||
const float win_size_x, const float win_size_y,
|
const float win_size_x, const float win_size_y,
|
||||||
const float win_pos_x, const float win_pos_y);
|
const float win_pos_x, const float win_pos_y);
|
||||||
@ -410,20 +410,20 @@ private:
|
|||||||
std::string m_export_dir_path;
|
std::string m_export_dir_path;
|
||||||
protected:
|
protected:
|
||||||
// Reserves space on right for more buttons
|
// Reserves space on right for more buttons
|
||||||
virtual void count_spaces() override;
|
void count_spaces() override;
|
||||||
virtual void render_text(ImGuiWrapper& imgui,
|
void render_text(ImGuiWrapper& imgui,
|
||||||
const float win_size_x, const float win_size_y,
|
const float win_size_x, const float win_size_y,
|
||||||
const float win_pos_x, const float win_pos_y) override;
|
const float win_pos_x, const float win_pos_y) override;
|
||||||
// Renders also button to open directory with exported path and eject removable media
|
// Renders also button to open directory with exported path and eject removable media
|
||||||
virtual void render_close_button(ImGuiWrapper& imgui,
|
void render_close_button(ImGuiWrapper& imgui,
|
||||||
const float win_size_x, const float win_size_y,
|
const float win_size_x, const float win_size_y,
|
||||||
const float win_pos_x, const float win_pos_y) override;
|
const float win_pos_x, const float win_pos_y) override;
|
||||||
void render_eject_button(ImGuiWrapper& imgui,
|
void render_eject_button(ImGuiWrapper& imgui,
|
||||||
const float win_size_x, const float win_size_y,
|
const float win_size_x, const float win_size_y,
|
||||||
const float win_pos_x, const float win_pos_y);
|
const float win_pos_x, const float win_pos_y);
|
||||||
virtual void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
|
void render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y) override
|
||||||
{ m_minimize_b_visible = false; }
|
{ m_minimize_b_visible = false; }
|
||||||
virtual bool on_text_click() override;
|
bool on_text_click() override;
|
||||||
// local time of last hover for showing tooltip
|
// local time of last hover for showing tooltip
|
||||||
long m_hover_time { 0 };
|
long m_hover_time { 0 };
|
||||||
};
|
};
|
||||||
@ -465,23 +465,7 @@ private:
|
|||||||
#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT
|
#endif // ENABLE_NEW_NOTIFICATIONS_FADE_OUT
|
||||||
*/
|
*/
|
||||||
//prepared (basic) notifications
|
//prepared (basic) notifications
|
||||||
const std::vector<NotificationData> basic_notifications = {
|
static const NotificationData basic_notifications[];
|
||||||
// {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")},
|
|
||||||
// {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") },
|
|
||||||
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
|
|
||||||
// {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") },
|
|
||||||
// {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") },
|
|
||||||
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."), [](wxEvtHandler* evnthndlr){
|
|
||||||
if (evnthndlr != nullptr) wxPostEvent(evnthndlr, PresetUpdateAvailableClickedEvent(EVT_PRESET_UPDATE_AVAILABLE_CLICKED)); return true; }},
|
|
||||||
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr){
|
|
||||||
wxLaunchDefaultBrowser("https://github.com/" SLIC3R_GITHUB "/releases"); return true; }},
|
|
||||||
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotification, 10,
|
|
||||||
_u8L("You have just added a G-code for color change, but its value is empty.\n"
|
|
||||||
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
|
||||||
//{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotification, 20, _u8L("New vesion of PrusaSlicer is available.", _u8L("Download page.") },
|
|
||||||
//{NotificationType::LoadingFailed, NotificationLevel::RegularNotification, 20, _u8L("Loading of model has Failed") },
|
|
||||||
//{NotificationType::DeviceEjected, NotificationLevel::RegularNotification, 10, _u8L("Removable device has been safely ejected")} // if we want changeble text (like here name of device), we need to do it as CustomNotification
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}//namespace GUI
|
}//namespace GUI
|
||||||
|
@ -977,6 +977,8 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
|||||||
ret = static_cast<int>(config.option<ConfigOptionEnum<WipeAlgo>>(opt_key)->value);
|
ret = static_cast<int>(config.option<ConfigOptionEnum<WipeAlgo>>(opt_key)->value);
|
||||||
} else if (opt_key == "output_format") {
|
} else if (opt_key == "output_format") {
|
||||||
ret = static_cast<int>(config.option<ConfigOptionEnum<OutputFormat>>(opt_key)->value);
|
ret = static_cast<int>(config.option<ConfigOptionEnum<OutputFormat>>(opt_key)->value);
|
||||||
|
} else if (opt_key == "config_compatibility") {
|
||||||
|
ret = static_cast<int>(config.option<ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>>(opt_key)->value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -67,7 +67,8 @@ PresetForPrinter::PresetForPrinter(PhysicalPrinterDialog* parent, const std::str
|
|||||||
// update Print Host upload from the selected preset
|
// update Print Host upload from the selected preset
|
||||||
m_parent->get_printer()->update_from_preset(*preset);
|
m_parent->get_printer()->update_from_preset(*preset);
|
||||||
// update values in parent (PhysicalPrinterDialog)
|
// update values in parent (PhysicalPrinterDialog)
|
||||||
m_parent->update();
|
m_parent->update(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// update PrinterTechnology if it was changed
|
// update PrinterTechnology if it was changed
|
||||||
@ -153,7 +154,8 @@ void PresetForPrinter::msw_rescale()
|
|||||||
|
|
||||||
PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_name) :
|
PhysicalPrinterDialog::PhysicalPrinterDialog(wxWindow* parent, wxString printer_name) :
|
||||||
DPIDialog(parent, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
DPIDialog(parent, wxID_ANY, _L("Physical Printer"), wxDefaultPosition, wxSize(45 * wxGetApp().em_unit(), -1), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
||||||
m_printer("", wxGetApp().preset_bundle->physical_printers.default_config())
|
m_printer("", wxGetApp().preset_bundle->physical_printers.default_config()),
|
||||||
|
had_all_mk3(!printer_name.empty())
|
||||||
{
|
{
|
||||||
SetFont(wxGetApp().normal_font());
|
SetFont(wxGetApp().normal_font());
|
||||||
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||||
@ -484,7 +486,7 @@ void PhysicalPrinterDialog::update_printhost_buttons()
|
|||||||
m_printhost_browse_btn->Enable(host->has_auto_discovery());
|
m_printhost_browse_btn->Enable(host->has_auto_discovery());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalPrinterDialog::update()
|
void PhysicalPrinterDialog::update(bool printer_change)
|
||||||
{
|
{
|
||||||
m_optgroup->reload_config();
|
m_optgroup->reload_config();
|
||||||
|
|
||||||
@ -492,19 +494,30 @@ void PhysicalPrinterDialog::update()
|
|||||||
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
|
// Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment)
|
||||||
bool supports_multiple_printers = false;
|
bool supports_multiple_printers = false;
|
||||||
if (tech == ptFFF) {
|
if (tech == ptFFF) {
|
||||||
m_optgroup->show_field("host_type");
|
update_host_type(printer_change);
|
||||||
m_optgroup->hide_field("printhost_authorization_type");
|
|
||||||
m_optgroup->show_field("printhost_apikey", true);
|
|
||||||
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
|
||||||
m_optgroup->hide_field(opt_key);
|
|
||||||
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||||
supports_multiple_printers = opt && opt->value == htRepetier;
|
m_optgroup->show_field("host_type");
|
||||||
|
if (opt && opt->value == htPrusaLink)
|
||||||
|
{
|
||||||
|
m_optgroup->show_field("printhost_authorization_type");
|
||||||
|
AuthorizationType auth_type = m_config->option<ConfigOptionEnum<AuthorizationType>>("printhost_authorization_type")->value;
|
||||||
|
m_optgroup->show_field("printhost_apikey", auth_type == AuthorizationType::atKeyPassword);
|
||||||
|
for (const char* opt_key : { "printhost_user", "printhost_password" })
|
||||||
|
m_optgroup->show_field(opt_key, auth_type == AuthorizationType::atUserPassword);
|
||||||
|
} else {
|
||||||
|
m_optgroup->hide_field("printhost_authorization_type");
|
||||||
|
m_optgroup->show_field("printhost_apikey", true);
|
||||||
|
for (const std::string& opt_key : std::vector<std::string>{ "printhost_user", "printhost_password" })
|
||||||
|
m_optgroup->hide_field(opt_key);
|
||||||
|
supports_multiple_printers = opt && opt->value == htRepetier;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// hide api key for klipper
|
// hide api key for klipper
|
||||||
if (opt && opt->value == htKlipper) {
|
if (opt && opt->value == htKlipper) {
|
||||||
m_optgroup->hide_field("printhost_apikey");
|
m_optgroup->hide_field("printhost_apikey");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
|
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
|
||||||
m_optgroup->hide_field("host_type");
|
m_optgroup->hide_field("host_type");
|
||||||
@ -534,6 +547,58 @@ void PhysicalPrinterDialog::update()
|
|||||||
this->Layout();
|
this->Layout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PhysicalPrinterDialog::update_host_type(bool printer_change)
|
||||||
|
{
|
||||||
|
if (m_presets.empty())
|
||||||
|
return;
|
||||||
|
bool all_presets_are_from_mk3_family = true;
|
||||||
|
|
||||||
|
for (PresetForPrinter* prstft : m_presets) {
|
||||||
|
std::string preset_name = prstft->get_preset_name();
|
||||||
|
if (Preset* preset = wxGetApp().preset_bundle->printers.find_preset(preset_name)) {
|
||||||
|
std::string model_id = preset->config.opt_string("printer_model");
|
||||||
|
if (preset->vendor && preset->vendor->name == "Prusa Research") {
|
||||||
|
const std::vector<VendorProfile::PrinterModel>& models = preset->vendor->models;
|
||||||
|
auto it = std::find_if(models.begin(), models.end(),
|
||||||
|
[model_id](const VendorProfile::PrinterModel& model) { return model.id == model_id; });
|
||||||
|
if (it != models.end() && it->family == "MK3")
|
||||||
|
continue;
|
||||||
|
} else if (!preset->vendor && model_id.rfind("MK3", 0) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
all_presets_are_from_mk3_family = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Field* ht = m_optgroup->get_field("host_type");
|
||||||
|
|
||||||
|
wxArrayString types;
|
||||||
|
// Append localized enum_labels
|
||||||
|
assert(ht->m_opt.enum_labels.size() == ht->m_opt.enum_values.size());
|
||||||
|
for (size_t i = 0; i < ht->m_opt.enum_labels.size(); i++) {
|
||||||
|
if (ht->m_opt.enum_values[i] == "prusalink" && !all_presets_are_from_mk3_family)
|
||||||
|
continue;
|
||||||
|
types.Add(_(ht->m_opt.enum_labels[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
Choice* choice = dynamic_cast<Choice*>(ht);
|
||||||
|
choice->set_values(types);
|
||||||
|
auto set_to_choice_and_config = [this, choice](PrintHostType type) {
|
||||||
|
choice->set_value(static_cast<int>(type));
|
||||||
|
m_config->set_key_value("host_type", new ConfigOptionEnum<PrintHostType>(type));
|
||||||
|
};
|
||||||
|
if ((printer_change && all_presets_are_from_mk3_family) || (!had_all_mk3 && all_presets_are_from_mk3_family))
|
||||||
|
set_to_choice_and_config(htPrusaLink);
|
||||||
|
else if ((printer_change && !all_presets_are_from_mk3_family) || (!all_presets_are_from_mk3_family && m_config->option<ConfigOptionEnum<PrintHostType>>("host_type")->value == htPrusaLink))
|
||||||
|
set_to_choice_and_config(htOctoPrint);
|
||||||
|
else
|
||||||
|
choice->set_value(m_config->option("host_type")->getInt());
|
||||||
|
had_all_mk3 = all_presets_are_from_mk3_family;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
wxString PhysicalPrinterDialog::get_printer_name()
|
wxString PhysicalPrinterDialog::get_printer_name()
|
||||||
{
|
{
|
||||||
return m_printer_name->GetValue();
|
return m_printer_name->GetValue();
|
||||||
@ -661,8 +726,9 @@ void PhysicalPrinterDialog::AddPreset(wxEvent& event)
|
|||||||
|
|
||||||
m_presets_sizer->Add(m_presets.back()->sizer(), 1, wxEXPAND | wxTOP, BORDER_W);
|
m_presets_sizer->Add(m_presets.back()->sizer(), 1, wxEXPAND | wxTOP, BORDER_W);
|
||||||
update_full_printer_names();
|
update_full_printer_names();
|
||||||
|
|
||||||
this->Fit();
|
this->Fit();
|
||||||
|
|
||||||
|
update_host_type(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer)
|
void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer)
|
||||||
@ -689,7 +755,8 @@ void PhysicalPrinterDialog::DeletePreset(PresetForPrinter* preset_for_printer)
|
|||||||
|
|
||||||
this->Layout();
|
this->Layout();
|
||||||
this->Fit();
|
this->Fit();
|
||||||
|
|
||||||
|
update_host_type(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}} // namespace Slic3r::GUI
|
}} // namespace Slic3r::GUI
|
||||||
|
@ -85,7 +85,8 @@ public:
|
|||||||
PhysicalPrinterDialog(wxWindow* parent, wxString printer_name);
|
PhysicalPrinterDialog(wxWindow* parent, wxString printer_name);
|
||||||
~PhysicalPrinterDialog();
|
~PhysicalPrinterDialog();
|
||||||
|
|
||||||
void update();
|
void update(bool printer_change = false);
|
||||||
|
void update_host_type(bool printer_change);
|
||||||
void update_printhost_buttons();
|
void update_printhost_buttons();
|
||||||
void update_printers();
|
void update_printers();
|
||||||
wxString get_printer_name();
|
wxString get_printer_name();
|
||||||
@ -95,10 +96,11 @@ public:
|
|||||||
PrinterTechnology get_printer_technology();
|
PrinterTechnology get_printer_technology();
|
||||||
|
|
||||||
void DeletePreset(PresetForPrinter* preset_for_printer);
|
void DeletePreset(PresetForPrinter* preset_for_printer);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||||
void on_sys_color_changed() override {};
|
void on_sys_color_changed() override {};
|
||||||
|
|
||||||
|
bool had_all_mk3;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -2399,7 +2399,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
{
|
{
|
||||||
DynamicPrintConfig config_loaded;
|
DynamicPrintConfig config_loaded;
|
||||||
model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, false, load_config);
|
ConfigSubstitutionContext config_substitutions{ ForwardCompatibilitySubstitutionRule::Enable };
|
||||||
|
model = Slic3r::Model::read_from_archive(path.string(), &config_loaded, &config_substitutions, only_if(load_config, Model::LoadAttribute::CheckVersion));
|
||||||
if (load_config && !config_loaded.empty()) {
|
if (load_config && !config_loaded.empty()) {
|
||||||
// Based on the printer technology field found in the loaded config, select the base for the config,
|
// Based on the printer technology field found in the loaded config, select the base for the config,
|
||||||
PrinterTechnology printer_technology = Preset::printer_technology(config_loaded);
|
PrinterTechnology printer_technology = Preset::printer_technology(config_loaded);
|
||||||
@ -2425,6 +2426,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
// and place the loaded config over the base.
|
// and place the loaded config over the base.
|
||||||
config += std::move(config_loaded);
|
config += std::move(config_loaded);
|
||||||
}
|
}
|
||||||
|
if (! config_substitutions.empty())
|
||||||
|
show_substitutions_info(config_substitutions.substitutions, filename.string());
|
||||||
|
|
||||||
this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
|
this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
|
||||||
}
|
}
|
||||||
@ -2445,11 +2448,15 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
model = Slic3r::Model::read_from_file(path.string(), nullptr, false, load_config);
|
model = Slic3r::Model::read_from_file(path.string(), nullptr, nullptr, only_if(load_config, Model::LoadAttribute::CheckVersion));
|
||||||
for (auto obj : model.objects)
|
for (auto obj : model.objects)
|
||||||
if (obj->name.empty())
|
if (obj->name.empty())
|
||||||
obj->name = fs::path(obj->input_file).filename().string();
|
obj->name = fs::path(obj->input_file).filename().string();
|
||||||
}
|
}
|
||||||
|
} catch (const ConfigurationError &e) {
|
||||||
|
std::string message = GUI::format(_L("Failed loading file \"%1%\" due to an invalid configuration."), filename.string()) + "\n\n" + e.what();
|
||||||
|
GUI::show_error(q, message);
|
||||||
|
continue;
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
GUI::show_error(q, e.what());
|
GUI::show_error(q, e.what());
|
||||||
continue;
|
continue;
|
||||||
@ -3316,7 +3323,7 @@ void Plater::priv::reload_from_disk()
|
|||||||
Model new_model;
|
Model new_model;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
new_model = Model::read_from_file(path, nullptr, true, false);
|
new_model = Model::read_from_file(path, nullptr, nullptr, Model::LoadAttribute::AddDefaultInstances);
|
||||||
for (ModelObject* model_object : new_model.objects)
|
for (ModelObject* model_object : new_model.objects)
|
||||||
{
|
{
|
||||||
model_object->center_around_origin();
|
model_object->center_around_origin();
|
||||||
@ -4706,7 +4713,9 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
|||||||
// Switch to the other printer technology. Switch to the last printer active for that particular technology.
|
// Switch to the other printer technology. Switch to the last printer active for that particular technology.
|
||||||
AppConfig *app_config = wxGetApp().app_config;
|
AppConfig *app_config = wxGetApp().app_config;
|
||||||
app_config->set("presets", "printer", (new_printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name);
|
app_config->set("presets", "printer", (new_printer_technology == ptFFF) ? m_last_fff_printer_profile_name : m_last_sla_printer_profile_name);
|
||||||
wxGetApp().preset_bundle->load_presets(*app_config);
|
//FIXME Why are we reloading the whole preset bundle here? Please document. This is fishy and it is unnecessarily expensive.
|
||||||
|
// Anyways, don't report any config value substitutions, they have been already reported to the user at application start up.
|
||||||
|
wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||||
// load_current_presets() calls Tab::load_current_preset() -> TabPrint::update() -> Object_list::update_and_show_object_settings_item(),
|
// load_current_presets() calls Tab::load_current_preset() -> TabPrint::update() -> Object_list::update_and_show_object_settings_item(),
|
||||||
// but the Object list still keeps pointer to the old Model. Avoid a crash by removing selection first.
|
// but the Object list still keeps pointer to the old Model. Avoid a crash by removing selection first.
|
||||||
this->sidebar->obj_list()->unselect_objects();
|
this->sidebar->obj_list()->unselect_objects();
|
||||||
@ -5477,13 +5486,14 @@ void Plater::export_gcode(bool prefer_removable)
|
|||||||
|
|
||||||
fs::path output_path;
|
fs::path output_path;
|
||||||
{
|
{
|
||||||
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 file as:"),
|
std::string ext = default_output_file.extension().string();
|
||||||
|
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"),
|
||||||
start_dir,
|
start_dir,
|
||||||
from_path(default_output_file.filename()),
|
from_path(default_output_file.filename()),
|
||||||
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()),
|
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : boost::iequals(ext, ".sl1s") ? FT_SL1S : FT_SL1, ext),
|
||||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
|
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
|
||||||
);
|
);
|
||||||
if (dlg.ShowModal() == wxID_OK)
|
if (dlg.ShowModal() == wxID_OK)
|
||||||
output_path = into_path(dlg.GetPath());
|
output_path = into_path(dlg.GetPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,9 +887,13 @@ void PlaterPresetComboBox::update()
|
|||||||
if (!tooltip.IsEmpty())
|
if (!tooltip.IsEmpty())
|
||||||
SetToolTip(tooltip);
|
SetToolTip(tooltip);
|
||||||
|
|
||||||
|
#ifdef __WXMSW__
|
||||||
|
// Use this part of code just on Windows to avoid of some layout issues on Linux
|
||||||
|
// see https://github.com/prusa3d/PrusaSlicer/issues/5163 and https://github.com/prusa3d/PrusaSlicer/issues/5505
|
||||||
// Update control min size after rescale (changed Display DPI under MSW)
|
// Update control min size after rescale (changed Display DPI under MSW)
|
||||||
if (GetMinWidth() != 20 * m_em_unit)
|
if (GetMinWidth() != 20 * m_em_unit)
|
||||||
SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight()));
|
SetMinSize(wxSize(20 * m_em_unit, GetSize().GetHeight()));
|
||||||
|
#endif //__WXMSW__
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlaterPresetComboBox::msw_rescale()
|
void PlaterPresetComboBox::msw_rescale()
|
||||||
|
@ -952,6 +952,8 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
|
|||||||
return get_string_from_enum<InfillPattern>(opt_key, config);
|
return get_string_from_enum<InfillPattern>(opt_key, config);
|
||||||
if (opt_key == "complete_objects_sort")
|
if (opt_key == "complete_objects_sort")
|
||||||
return get_string_from_enum<CompleteObjectSort>(opt_key, config);
|
return get_string_from_enum<CompleteObjectSort>(opt_key, config);
|
||||||
|
if (opt_key == "config_compatibility")
|
||||||
|
return get_string_from_enum<ForwardCompatibilitySubstitutionRule>(opt_key, config);
|
||||||
if (opt_key == "display_orientation")
|
if (opt_key == "display_orientation")
|
||||||
return get_string_from_enum<SLADisplayOrientation>(opt_key, config);
|
return get_string_from_enum<SLADisplayOrientation>(opt_key, config);
|
||||||
if (opt_key == "output_format")
|
if (opt_key == "output_format")
|
||||||
|
@ -85,8 +85,11 @@ bool MsgUpdateSlic3r::disable_version_check() const
|
|||||||
|
|
||||||
// MsgUpdateConfig
|
// MsgUpdateConfig
|
||||||
|
|
||||||
MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates) :
|
MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_before_wizard/* = false*/) :
|
||||||
MsgDialog(nullptr, _(L("Configuration update")), _(L("Configuration update is available")), wxID_NONE)
|
MsgDialog(nullptr, force_before_wizard ? _L("Opening Configuration Wizard") : _L("Configuration update"),
|
||||||
|
force_before_wizard ? _L("PrusaSlicer is not using the newest configuration available.\n"
|
||||||
|
"Configuration Wizard may not offer the latest printers, filaments and SLA materials to be installed. ") :
|
||||||
|
_L("Configuration update is available"), wxID_NONE)
|
||||||
{
|
{
|
||||||
auto *text = new wxStaticText(this, wxID_ANY, _(L(
|
auto *text = new wxStaticText(this, wxID_ANY, _(L(
|
||||||
"Would you like to install it?\n\n"
|
"Would you like to install it?\n\n"
|
||||||
@ -130,11 +133,17 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates) :
|
|||||||
content_sizer->Add(versions);
|
content_sizer->Add(versions);
|
||||||
content_sizer->AddSpacer(2*VERT_SPACING);
|
content_sizer->AddSpacer(2*VERT_SPACING);
|
||||||
|
|
||||||
auto *btn_cancel = new wxButton(this, wxID_CANCEL);
|
auto* btn_ok = new wxButton(this, wxID_OK, force_before_wizard ? _L("Install") : "OK");
|
||||||
btn_sizer->Add(btn_cancel);
|
|
||||||
btn_sizer->AddSpacer(HORIZ_SPACING);
|
|
||||||
auto *btn_ok = new wxButton(this, wxID_OK);
|
|
||||||
btn_sizer->Add(btn_ok);
|
btn_sizer->Add(btn_ok);
|
||||||
|
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||||
|
if (force_before_wizard) {
|
||||||
|
auto* btn_no_install = new wxButton(this, wxID_ANY, _L("Don't install"));
|
||||||
|
btn_no_install->Bind(wxEVT_BUTTON, [this](wxEvent&) { this->EndModal(wxID_CLOSE); });
|
||||||
|
btn_sizer->Add(btn_no_install);
|
||||||
|
btn_sizer->AddSpacer(HORIZ_SPACING);
|
||||||
|
}
|
||||||
|
auto* btn_cancel = new wxButton(this, wxID_CANCEL);
|
||||||
|
btn_sizer->Add(btn_cancel);
|
||||||
btn_ok->SetFocus();
|
btn_ok->SetFocus();
|
||||||
|
|
||||||
Fit();
|
Fit();
|
||||||
|
@ -54,7 +54,8 @@ public:
|
|||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
MsgUpdateConfig(const std::vector<Update> &updates);
|
// force_before_wizard - indicates that check of updated is forced before ConfigWizard opening
|
||||||
|
MsgUpdateConfig(const std::vector<Update> &updates, bool force_before_wizard = false);
|
||||||
MsgUpdateConfig(MsgUpdateConfig &&) = delete;
|
MsgUpdateConfig(MsgUpdateConfig &&) = delete;
|
||||||
MsgUpdateConfig(const MsgUpdateConfig &) = delete;
|
MsgUpdateConfig(const MsgUpdateConfig &) = delete;
|
||||||
MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete;
|
MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete;
|
||||||
|
@ -377,7 +377,8 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
|
|||||||
// PresetBundle bundle;
|
// PresetBundle bundle;
|
||||||
on_progress(L("Loading repaired model"), 80);
|
on_progress(L("Loading repaired model"), 80);
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &config, &model, false);
|
ConfigSubstitutionContext config_substitutions{ ForwardCompatibilitySubstitutionRule::EnableSilent };
|
||||||
|
bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), config, config_substitutions, &model, false);
|
||||||
boost::filesystem::remove(path_dst);
|
boost::filesystem::remove(path_dst);
|
||||||
if (! loaded)
|
if (! loaded)
|
||||||
throw Slic3r::RuntimeError(L("Import of the repaired 3mf file failed"));
|
throw Slic3r::RuntimeError(L("Import of the repaired 3mf file failed"));
|
||||||
|
@ -19,6 +19,7 @@ static HexFile::DeviceKind parse_device_kind(const std::string &str)
|
|||||||
else if (str == "mk3") { return HexFile::DEV_MK3; }
|
else if (str == "mk3") { return HexFile::DEV_MK3; }
|
||||||
else if (str == "mm-control") { return HexFile::DEV_MM_CONTROL; }
|
else if (str == "mm-control") { return HexFile::DEV_MM_CONTROL; }
|
||||||
else if (str == "cw1") { return HexFile::DEV_CW1; }
|
else if (str == "cw1") { return HexFile::DEV_CW1; }
|
||||||
|
else if (str == "cw1s") { return HexFile::DEV_CW1S; }
|
||||||
else { return HexFile::DEV_GENERIC; }
|
else { return HexFile::DEV_GENERIC; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ struct HexFile
|
|||||||
DEV_MK3,
|
DEV_MK3,
|
||||||
DEV_MM_CONTROL,
|
DEV_MM_CONTROL,
|
||||||
DEV_CW1,
|
DEV_CW1,
|
||||||
|
DEV_CW1S,
|
||||||
};
|
};
|
||||||
|
|
||||||
boost::filesystem::path path;
|
boost::filesystem::path path;
|
||||||
|
@ -178,7 +178,7 @@ const char* SL1Host::get_name() const { return "SL1Host"; }
|
|||||||
|
|
||||||
wxString SL1Host::get_test_ok_msg () const
|
wxString SL1Host::get_test_ok_msg () const
|
||||||
{
|
{
|
||||||
return wxString::Format(_L("Connection to %s works correctly."), "Prusa SL1");
|
return wxString::Format(_L("Connection to %s works correctly."), "Prusa SL1 / SL1S");
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString SL1Host::get_test_failed_msg (wxString &msg) const
|
wxString SL1Host::get_test_failed_msg (wxString &msg) const
|
||||||
@ -209,4 +209,48 @@ void SL1Host::set_auth(Http &http) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrusaLink
|
||||||
|
PrusaLink::PrusaLink(DynamicPrintConfig* config) :
|
||||||
|
OctoPrint(config),
|
||||||
|
authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),
|
||||||
|
username(config->opt_string("printhost_user")),
|
||||||
|
password(config->opt_string("printhost_password"))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* PrusaLink::get_name() const { return "PrusaLink"; }
|
||||||
|
|
||||||
|
wxString PrusaLink::get_test_ok_msg() const
|
||||||
|
{
|
||||||
|
return _(L("Connection to PrusaLink works correctly."));
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString PrusaLink::get_test_failed_msg(wxString& msg) const
|
||||||
|
{
|
||||||
|
return GUI::from_u8((boost::format("%s: %s")
|
||||||
|
% _utf8(L("Could not connect to PrusaLink"))
|
||||||
|
% std::string(msg.ToUTF8())).str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrusaLink::validate_version_text(const boost::optional<std::string>& version_text) const
|
||||||
|
{
|
||||||
|
return version_text ? (boost::starts_with(*version_text, "PrusaLink") || boost::starts_with(*version_text, "OctoPrint")) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrusaLink::set_auth(Http& http) const
|
||||||
|
{
|
||||||
|
switch (authorization_type) {
|
||||||
|
case atKeyPassword:
|
||||||
|
http.header("X-Api-Key", get_apikey());
|
||||||
|
break;
|
||||||
|
case atUserPassword:
|
||||||
|
http.auth_digest(username, password);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get_cafile().empty()) {
|
||||||
|
http.ca_file(get_cafile());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,31 @@ private:
|
|||||||
std::string password;
|
std::string password;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PrusaLink : public OctoPrint
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrusaLink(DynamicPrintConfig* config);
|
||||||
|
~PrusaLink() override = default;
|
||||||
|
|
||||||
|
const char* get_name() const override;
|
||||||
|
|
||||||
|
wxString get_test_ok_msg() const override;
|
||||||
|
wxString get_test_failed_msg(wxString& msg) const override;
|
||||||
|
bool can_start_print() const override { return true; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool validate_version_text(const boost::optional<std::string>& version_text) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void set_auth(Http& http) const override;
|
||||||
|
|
||||||
|
// Host authorization type.
|
||||||
|
AuthorizationType authorization_type;
|
||||||
|
// username and password for HTTP Digest Authentization (RFC RFC2617)
|
||||||
|
std::string username;
|
||||||
|
std::string password;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -56,15 +56,18 @@ static const char *TMP_EXTENSION = ".download";
|
|||||||
|
|
||||||
void copy_file_fix(const fs::path &source, const fs::path &target)
|
void copy_file_fix(const fs::path &source, const fs::path &target)
|
||||||
{
|
{
|
||||||
static const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; // aka 644
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << format("PresetUpdater: Copying %1% -> %2%", source, target);
|
BOOST_LOG_TRIVIAL(debug) << format("PresetUpdater: Copying %1% -> %2%", source, target);
|
||||||
|
std::string error_message;
|
||||||
// Make sure the file has correct permission both before and after we copy over it
|
CopyFileResult cfr = copy_file(source.string(), target.string(), error_message, false);
|
||||||
if (fs::exists(target)) {
|
if (cfr != CopyFileResult::SUCCESS) {
|
||||||
fs::permissions(target, perms);
|
BOOST_LOG_TRIVIAL(error) << "Copying failed(" << cfr << "): " << error_message;
|
||||||
|
throw Slic3r::CriticalException(GUI::format(
|
||||||
|
_L("Copying of file %1% to %2% failed: %3%"),
|
||||||
|
source, target, error_message));
|
||||||
}
|
}
|
||||||
fs::copy_file(source, target, fs::copy_option::overwrite_if_exists);
|
// Permissions should be copied from the source file by copy_file(). We are not sure about the source
|
||||||
|
// permissions, let's rewrite them with 644.
|
||||||
|
static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read;
|
||||||
fs::permissions(target, perms);
|
fs::permissions(target, perms);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +171,7 @@ struct PresetUpdater::priv
|
|||||||
|
|
||||||
void check_install_indices() const;
|
void check_install_indices() const;
|
||||||
Updates get_config_updates(const Semver& old_slic3r_version) const;
|
Updates get_config_updates(const Semver& old_slic3r_version) const;
|
||||||
void perform_updates(Updates &&updates, bool snapshot = true) const;
|
bool perform_updates(Updates &&updates, bool snapshot = true) const;
|
||||||
void set_waiting_updates(Updates u);
|
void set_waiting_updates(Updates u);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -474,7 +477,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (recommended->config_version < vp.config_version) {
|
if (recommended->config_version < vp.config_version) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << format("Recommended config version for the currently running " SLIC3R_APP_NAME " is older than the currently installed config for vendor %1%. This should not happen.", idx.vendor());
|
BOOST_LOG_TRIVIAL(warning) << format("Recommended config version for the currently running %1% is older than the currently installed config for vendor %2%. This should not happen.", SLIC3R_APP_NAME, idx.vendor());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,7 +548,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||||||
found = true;
|
found = true;
|
||||||
} else {
|
} else {
|
||||||
BOOST_LOG_TRIVIAL(warning) << format("The recommended config version for vendor `%1%` in resources does not match the recommended\n"
|
BOOST_LOG_TRIVIAL(warning) << format("The recommended config version for vendor `%1%` in resources does not match the recommended\n"
|
||||||
" config version for this version of " SLIC3R_APP_NAME ". Corrupted installation?", idx.vendor());
|
" config version for this version of `%2%`. Corrupted installation?", idx.vendor(), SLIC3R_APP_NAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -598,12 +601,14 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
|
|||||||
return updates;
|
return updates;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const
|
bool PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) const
|
||||||
{
|
{
|
||||||
if (updates.incompats.size() > 0) {
|
if (updates.incompats.size() > 0) {
|
||||||
if (snapshot) {
|
if (snapshot) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
||||||
SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE);
|
if (! GUI::Config::take_config_snapshot_cancel_on_error(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_DOWNGRADE, "",
|
||||||
|
_u8L("Continue and install configuration updates?")))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Deleting %1% incompatible bundles", updates.incompats.size());
|
BOOST_LOG_TRIVIAL(info) << format("Deleting %1% incompatible bundles", updates.incompats.size());
|
||||||
@ -618,7 +623,9 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||||||
|
|
||||||
if (snapshot) {
|
if (snapshot) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
BOOST_LOG_TRIVIAL(info) << "Taking a snapshot...";
|
||||||
SnapshotDB::singleton().take_snapshot(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE);
|
if (! GUI::Config::take_config_snapshot_cancel_on_error(*GUI::wxGetApp().app_config, Snapshot::SNAPSHOT_UPGRADE, "",
|
||||||
|
_u8L("Continue and install configuration updates?")))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Performing %1% updates", updates.updates.size());
|
BOOST_LOG_TRIVIAL(info) << format("Performing %1% updates", updates.updates.size());
|
||||||
@ -629,7 +636,8 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||||||
update.install();
|
update.install();
|
||||||
|
|
||||||
PresetBundle bundle;
|
PresetBundle bundle;
|
||||||
bundle.load_configbundle(update.source.string(), PresetBundle::LOAD_CFGBNDLE_SYSTEM);
|
// Throw when parsing invalid configuration. Only valid configuration is supposed to be provided over the air.
|
||||||
|
bundle.load_configbundle(update.source.string(), PresetBundle::LoadConfigBundleAttribute::LoadSystem, ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Deleting %1% conflicting presets", bundle.prints.size() + bundle.filaments.size() + bundle.printers.size());
|
BOOST_LOG_TRIVIAL(info) << format("Deleting %1% conflicting presets", bundle.prints.size() + bundle.filaments.size() + bundle.printers.size());
|
||||||
|
|
||||||
@ -661,6 +669,8 @@ void PresetUpdater::priv::perform_updates(Updates &&updates, bool snapshot) cons
|
|||||||
for (const auto &name : bundle.obsolete_presets.printers) { obsolete_remover("printer", name); }
|
for (const auto &name : bundle.obsolete_presets.printers) { obsolete_remover("printer", name); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetUpdater::priv::set_waiting_updates(Updates u)
|
void PresetUpdater::priv::set_waiting_updates(Updates u)
|
||||||
@ -727,7 +737,20 @@ void PresetUpdater::slic3r_update_notify()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, bool no_notification) const
|
static void reload_configs_update_gui()
|
||||||
|
{
|
||||||
|
// Reload global configuration
|
||||||
|
auto* app_config = GUI::wxGetApp().app_config;
|
||||||
|
// System profiles should not trigger any substitutions, user profiles may trigger substitutions, but these substitutions
|
||||||
|
// were already presented to the user on application start up. Just do substitutions now and keep quiet about it.
|
||||||
|
// However throw on substitutions in system profiles, those shall never happen with system profiles installed over the air.
|
||||||
|
GUI::wxGetApp().preset_bundle->load_presets(*app_config, ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem);
|
||||||
|
GUI::wxGetApp().load_current_presets();
|
||||||
|
GUI::wxGetApp().plater()->set_bed_shape();
|
||||||
|
GUI::wxGetApp().update_wizard_from_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params) const
|
||||||
{
|
{
|
||||||
if (! p->enabled_config_update) { return R_NOOP; }
|
if (! p->enabled_config_update) { return R_NOOP; }
|
||||||
|
|
||||||
@ -761,11 +784,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
|
|||||||
|
|
||||||
// This effectively removes the incompatible bundles:
|
// This effectively removes the incompatible bundles:
|
||||||
// (snapshot is taken beforehand)
|
// (snapshot is taken beforehand)
|
||||||
p->perform_updates(std::move(updates));
|
if (! p->perform_updates(std::move(updates)) ||
|
||||||
|
! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT))
|
||||||
if (!GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT)) {
|
|
||||||
return R_INCOMPAT_EXIT;
|
return R_INCOMPAT_EXIT;
|
||||||
}
|
|
||||||
|
|
||||||
return R_INCOMPAT_CONFIGURED;
|
return R_INCOMPAT_CONFIGURED;
|
||||||
}
|
}
|
||||||
@ -784,7 +805,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
|
|||||||
}
|
}
|
||||||
|
|
||||||
//forced update
|
//forced update
|
||||||
if(incompatible_version)
|
if (incompatible_version)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. At least one requires higher version of Slicer.", updates.updates.size());
|
BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. At least one requires higher version of Slicer.", updates.updates.size());
|
||||||
|
|
||||||
@ -799,14 +820,9 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
|
|||||||
const auto res = dlg.ShowModal();
|
const auto res = dlg.ShowModal();
|
||||||
if (res == wxID_OK) {
|
if (res == wxID_OK) {
|
||||||
BOOST_LOG_TRIVIAL(info) << "User wants to update...";
|
BOOST_LOG_TRIVIAL(info) << "User wants to update...";
|
||||||
|
if (! p->perform_updates(std::move(updates)))
|
||||||
p->perform_updates(std::move(updates));
|
return R_INCOMPAT_EXIT;
|
||||||
|
reload_configs_update_gui();
|
||||||
// Reload global configuration
|
|
||||||
auto* app_config = GUI::wxGetApp().app_config;
|
|
||||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config);
|
|
||||||
GUI::wxGetApp().load_current_presets();
|
|
||||||
GUI::wxGetApp().plater()->set_bed_shape();
|
|
||||||
return R_UPDATE_INSTALLED;
|
return R_UPDATE_INSTALLED;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -816,36 +832,36 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
|
|||||||
}
|
}
|
||||||
|
|
||||||
// regular update
|
// regular update
|
||||||
if (no_notification) {
|
if (params == UpdateParams::SHOW_NOTIFICATION) {
|
||||||
BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size());
|
|
||||||
|
|
||||||
std::vector<GUI::MsgUpdateConfig::Update> updates_msg;
|
|
||||||
for (const auto& update : updates.updates) {
|
|
||||||
std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
|
|
||||||
updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
|
|
||||||
}
|
|
||||||
|
|
||||||
GUI::MsgUpdateConfig dlg(updates_msg);
|
|
||||||
|
|
||||||
const auto res = dlg.ShowModal();
|
|
||||||
if (res == wxID_OK) {
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
|
|
||||||
p->perform_updates(std::move(updates));
|
|
||||||
|
|
||||||
// Reload global configuration
|
|
||||||
auto* app_config = GUI::wxGetApp().app_config;
|
|
||||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config);
|
|
||||||
GUI::wxGetApp().load_current_presets();
|
|
||||||
return R_UPDATE_INSTALLED;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "User refused the update";
|
|
||||||
return R_UPDATE_REJECT;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p->set_waiting_updates(updates);
|
p->set_waiting_updates(updates);
|
||||||
GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAvailable);
|
GUI::wxGetApp().plater()->get_notification_manager()->push_notification(GUI::NotificationType::PresetUpdateAvailable);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << format("Update of %1% bundles available. Asking for confirmation ...", p->waiting_updates.updates.size());
|
||||||
|
|
||||||
|
std::vector<GUI::MsgUpdateConfig::Update> updates_msg;
|
||||||
|
for (const auto& update : updates.updates) {
|
||||||
|
std::string changelog_url = update.version.config_version.prerelease() == nullptr ? update.changelog_url : std::string();
|
||||||
|
updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url));
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI::MsgUpdateConfig dlg(updates_msg, params == UpdateParams::FORCED_BEFORE_WIZARD);
|
||||||
|
|
||||||
|
const auto res = dlg.ShowModal();
|
||||||
|
if (res == wxID_OK) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
|
||||||
|
if (! p->perform_updates(std::move(updates)))
|
||||||
|
return R_ALL_CANCELED;
|
||||||
|
reload_configs_update_gui();
|
||||||
|
return R_UPDATE_INSTALLED;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "User refused the update";
|
||||||
|
if (params == UpdateParams::FORCED_BEFORE_WIZARD && res == wxID_CANCEL)
|
||||||
|
return R_ALL_CANCELED;
|
||||||
|
return R_UPDATE_REJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MsgUpdateConfig will show after the notificaation is clicked
|
// MsgUpdateConfig will show after the notificaation is clicked
|
||||||
} else {
|
} else {
|
||||||
@ -855,7 +871,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
|
|||||||
return R_NOOP;
|
return R_NOOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot) const
|
bool PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot) const
|
||||||
{
|
{
|
||||||
Updates updates;
|
Updates updates;
|
||||||
|
|
||||||
@ -867,7 +883,7 @@ void PresetUpdater::install_bundles_rsrc(std::vector<std::string> bundles, bool
|
|||||||
updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", "");
|
updates.updates.emplace_back(std::move(path_in_rsrc), std::move(path_in_vendors), Version(), "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
p->perform_updates(std::move(updates), snapshot);
|
return p->perform_updates(std::move(updates), snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PresetUpdater::on_update_notification_confirm()
|
void PresetUpdater::on_update_notification_confirm()
|
||||||
@ -887,20 +903,14 @@ void PresetUpdater::on_update_notification_confirm()
|
|||||||
const auto res = dlg.ShowModal();
|
const auto res = dlg.ShowModal();
|
||||||
if (res == wxID_OK) {
|
if (res == wxID_OK) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
|
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
|
||||||
p->perform_updates(std::move(p->waiting_updates));
|
if (p->perform_updates(std::move(p->waiting_updates))) {
|
||||||
|
reload_configs_update_gui();
|
||||||
// Reload global configuration
|
p->has_waiting_updates = false;
|
||||||
auto* app_config = GUI::wxGetApp().app_config;
|
}
|
||||||
GUI::wxGetApp().preset_bundle->load_presets(*app_config);
|
|
||||||
GUI::wxGetApp().load_current_presets();
|
|
||||||
p->has_waiting_updates = false;
|
|
||||||
//return R_UPDATE_INSTALLED;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BOOST_LOG_TRIVIAL(info) << "User refused the update";
|
BOOST_LOG_TRIVIAL(info) << "User refused the update";
|
||||||
//return R_UPDATE_REJECT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,18 +35,24 @@ public:
|
|||||||
R_INCOMPAT_CONFIGURED,
|
R_INCOMPAT_CONFIGURED,
|
||||||
R_UPDATE_INSTALLED,
|
R_UPDATE_INSTALLED,
|
||||||
R_UPDATE_REJECT,
|
R_UPDATE_REJECT,
|
||||||
R_UPDATE_NOTIFICATION
|
R_UPDATE_NOTIFICATION,
|
||||||
|
R_ALL_CANCELED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class UpdateParams {
|
||||||
|
SHOW_TEXT_BOX, // force modal textbox
|
||||||
|
SHOW_NOTIFICATION, // only shows notification
|
||||||
|
FORCED_BEFORE_WIZARD // indicates that check of updated is forced before ConfigWizard opening
|
||||||
};
|
};
|
||||||
|
|
||||||
// If updating is enabled, check if updates are available in cache, if so, ask about installation.
|
// If updating is enabled, check if updates are available in cache, if so, ask about installation.
|
||||||
// A false return value implies Slic3r should exit due to incompatibility of configuration.
|
// A false return value implies Slic3r should exit due to incompatibility of configuration.
|
||||||
// Providing old slic3r version upgrade profiles on upgrade of an application even in case
|
// Providing old slic3r version upgrade profiles on upgrade of an application even in case
|
||||||
// that the config index installed from the Internet is equal to the index contained in the installation package.
|
// that the config index installed from the Internet is equal to the index contained in the installation package.
|
||||||
// no_notification = force modal textbox, otherwise some cases only shows notification
|
UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params) const;
|
||||||
UpdateResult config_update(const Semver &old_slic3r_version, bool no_notification) const;
|
|
||||||
|
|
||||||
// "Update" a list of bundles from resources (behaves like an online update).
|
// "Update" a list of bundles from resources (behaves like an online update).
|
||||||
void install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot = true) const;
|
bool install_bundles_rsrc(std::vector<std::string> bundles, bool snapshot = true) const;
|
||||||
|
|
||||||
void on_update_notification_confirm();
|
void on_update_notification_confirm();
|
||||||
private:
|
private:
|
||||||
|
@ -60,6 +60,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
|||||||
const auto host_type = opt != nullptr ? opt->value : htOctoPrint;
|
const auto host_type = opt != nullptr ? opt->value : htOctoPrint;
|
||||||
|
|
||||||
switch (host_type) {
|
switch (host_type) {
|
||||||
|
case htPrusaLink: return new PrusaLink(config);
|
||||||
case htOctoPrint: return new OctoPrint(config);
|
case htOctoPrint: return new OctoPrint(config);
|
||||||
case htDuet: return new Duet(config);
|
case htDuet: return new Duet(config);
|
||||||
case htFlashAir: return new FlashAir(config);
|
case htFlashAir: return new FlashAir(config);
|
||||||
|
@ -203,14 +203,14 @@ void init_print(std::initializer_list<TriangleMesh> input_meshes, Slic3r::Print
|
|||||||
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments)
|
void init_print(std::initializer_list<TestMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments)
|
||||||
{
|
{
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.set_deserialize(config_items);
|
config.set_deserialize_strict(config_items);
|
||||||
init_print(meshes, print, model, config, comments);
|
init_print(meshes, print, model, config, comments);
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments)
|
void init_print(std::initializer_list<TriangleMesh> meshes, Slic3r::Print &print, Slic3r::Model &model, std::initializer_list<Slic3r::ConfigBase::SetDeserializeItem> config_items, bool comments)
|
||||||
{
|
{
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.set_deserialize(config_items);
|
config.set_deserialize_strict(config_items);
|
||||||
init_print(meshes, print, model, config, comments);
|
init_print(meshes, print, model, config, comments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ SCENARIO("Extrusion width specifics", "[Flow]") {
|
|||||||
GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
|
GIVEN("A config with a skirt, brim, some fill density, 3 perimeters, and 1 bottom solid layer and a 20mm cube mesh") {
|
||||||
// this is a sharedptr
|
// this is a sharedptr
|
||||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "brim_width", 2 },
|
{ "brim_width", 2 },
|
||||||
{ "skirts", 1 },
|
{ "skirts", 1 },
|
||||||
{ "perimeters", 3 },
|
{ "perimeters", 3 },
|
||||||
|
@ -10,7 +10,7 @@ SCENARIO("lift() is not ignored after unlift() at normal values of Z", "[GCodeWr
|
|||||||
GIVEN("A config from a file and a single extruder.") {
|
GIVEN("A config from a file and a single extruder.") {
|
||||||
GCodeWriter writer;
|
GCodeWriter writer;
|
||||||
GCodeConfig &config = writer.config;
|
GCodeConfig &config = writer.config;
|
||||||
config.load(std::string(TEST_DATA_DIR) + "/fff_print_tests/test_gcodewriter/config_lift_unlift.ini");
|
config.load(std::string(TEST_DATA_DIR) + "/fff_print_tests/test_gcodewriter/config_lift_unlift.ini", ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
|
|
||||||
std::vector<uint16_t> extruder_ids {0};
|
std::vector<uint16_t> extruder_ids {0};
|
||||||
writer.set_extruders(extruder_ids);
|
writer.set_extruders(extruder_ids);
|
||||||
|
@ -62,7 +62,7 @@ SCENARIO("Print: Skirt generation", "[Print]") {
|
|||||||
SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces to become internal.", "[Print]") {
|
SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces to become internal.", "[Print]") {
|
||||||
GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") {
|
GIVEN("sliced 20mm cube and config with top_solid_surfaces = 2 and bottom_solid_surfaces = 1") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "top_solid_layers", 2 },
|
{ "top_solid_layers", 2 },
|
||||||
{ "bottom_solid_layers", 1 },
|
{ "bottom_solid_layers", 1 },
|
||||||
{ "layer_height", 0.5 }, // get a known number of layers
|
{ "layer_height", 0.5 }, // get a known number of layers
|
||||||
|
@ -224,7 +224,7 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
|||||||
{
|
{
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||||
config.set_num_extruders(4);
|
config.set_num_extruders(4);
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "start_gcode", "; Extruder [current_extruder]" },
|
{ "start_gcode", "; Extruder [current_extruder]" },
|
||||||
{ "infill_extruder", 2 },
|
{ "infill_extruder", 2 },
|
||||||
{ "solid_infill_extruder", 2 },
|
{ "solid_infill_extruder", 2 },
|
||||||
|
@ -31,7 +31,7 @@ static int get_brim_tool(const std::string &gcode)
|
|||||||
|
|
||||||
TEST_CASE("Skirt height is honored", "[Skirt]") {
|
TEST_CASE("Skirt height is honored", "[Skirt]") {
|
||||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 1 },
|
{ "skirts", 1 },
|
||||||
{ "skirt_height", 5 },
|
{ "skirt_height", 5 },
|
||||||
{ "perimeters", 0 },
|
{ "perimeters", 0 },
|
||||||
@ -64,7 +64,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
GIVEN("A default configuration") {
|
GIVEN("A default configuration") {
|
||||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
config.set_num_extruders(4);
|
config.set_num_extruders(4);
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "support_material_speed", 99 },
|
{ "support_material_speed", 99 },
|
||||||
{ "first_layer_height", 0.3 },
|
{ "first_layer_height", 0.3 },
|
||||||
{ "gcode_comments", true },
|
{ "gcode_comments", true },
|
||||||
@ -78,7 +78,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
});
|
});
|
||||||
|
|
||||||
WHEN("Brim width is set to 5") {
|
WHEN("Brim width is set to 5") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "perimeters", 0 },
|
{ "perimeters", 0 },
|
||||||
{ "skirts", 0 },
|
{ "skirts", 0 },
|
||||||
{ "brim_width", 5 }
|
{ "brim_width", 5 }
|
||||||
@ -100,7 +100,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WHEN("Skirt area is smaller than the brim") {
|
WHEN("Skirt area is smaller than the brim") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 1 },
|
{ "skirts", 1 },
|
||||||
{ "brim_width", 10}
|
{ "brim_width", 10}
|
||||||
});
|
});
|
||||||
@ -110,7 +110,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WHEN("Skirt height is 0 and skirts > 0") {
|
WHEN("Skirt height is 0 and skirts > 0") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 2 },
|
{ "skirts", 2 },
|
||||||
{ "skirt_height", 0 }
|
{ "skirt_height", 0 }
|
||||||
});
|
});
|
||||||
@ -123,7 +123,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
// This is a real error! One shall print the brim with the external perimeter extruder!
|
// This is a real error! One shall print the brim with the external perimeter extruder!
|
||||||
WHEN("Perimeter extruder = 2 and support extruders = 3") {
|
WHEN("Perimeter extruder = 2 and support extruders = 3") {
|
||||||
THEN("Brim is printed with the extruder used for the perimeters of first object") {
|
THEN("Brim is printed with the extruder used for the perimeters of first object") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 0 },
|
{ "skirts", 0 },
|
||||||
{ "brim_width", 5 },
|
{ "brim_width", 5 },
|
||||||
{ "perimeter_extruder", 2 },
|
{ "perimeter_extruder", 2 },
|
||||||
@ -137,7 +137,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
}
|
}
|
||||||
WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
|
WHEN("Perimeter extruder = 2, support extruders = 3, raft is enabled") {
|
||||||
THEN("brim is printed with same extruder as skirt") {
|
THEN("brim is printed with same extruder as skirt") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 0 },
|
{ "skirts", 0 },
|
||||||
{ "brim_width", 5 },
|
{ "brim_width", 5 },
|
||||||
{ "perimeter_extruder", 2 },
|
{ "perimeter_extruder", 2 },
|
||||||
@ -153,7 +153,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
WHEN("brim width to 1 with layer_width of 0.5") {
|
WHEN("brim width to 1 with layer_width of 0.5") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 0 },
|
{ "skirts", 0 },
|
||||||
{ "first_layer_extrusion_width", 0.5 },
|
{ "first_layer_extrusion_width", 0.5 },
|
||||||
{ "brim_width", 1 }
|
{ "brim_width", 1 }
|
||||||
@ -167,7 +167,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
WHEN("brim ears on a square") {
|
WHEN("brim ears on a square") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 0 },
|
{ "skirts", 0 },
|
||||||
{ "first_layer_extrusion_width", 0.5 },
|
{ "first_layer_extrusion_width", 0.5 },
|
||||||
{ "brim_width", 1 },
|
{ "brim_width", 1 },
|
||||||
@ -182,7 +182,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WHEN("brim ears on a square but with a too small max angle") {
|
WHEN("brim ears on a square but with a too small max angle") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "skirts", 0 },
|
{ "skirts", 0 },
|
||||||
{ "first_layer_extrusion_width", 0.5 },
|
{ "first_layer_extrusion_width", 0.5 },
|
||||||
{ "brim_width", 1 },
|
{ "brim_width", 1 },
|
||||||
@ -198,7 +198,7 @@ SCENARIO("Original Slic3r Skirt/Brim tests", "[SkirtBrim]") {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
WHEN("Object is plated with overhang support and a brim") {
|
WHEN("Object is plated with overhang support and a brim") {
|
||||||
config.set_deserialize({
|
config.set_deserialize_strict({
|
||||||
{ "layer_height", 0.4 },
|
{ "layer_height", 0.4 },
|
||||||
{ "first_layer_height", 0.4 },
|
{ "first_layer_height", 0.4 },
|
||||||
{ "skirts", 1 },
|
{ "skirts", 1 },
|
||||||
|
@ -14,7 +14,8 @@ SCENARIO("Reading 3mf file", "[3mf]") {
|
|||||||
WHEN("3mf model is read") {
|
WHEN("3mf model is read") {
|
||||||
std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/Geräte/Büchse.3mf";
|
std::string path = std::string(TEST_DATA_DIR) + "/test_3mf/Geräte/Büchse.3mf";
|
||||||
DynamicPrintConfig config;
|
DynamicPrintConfig config;
|
||||||
bool ret = load_3mf(path.c_str(), &config, &model, false);
|
ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable };
|
||||||
|
bool ret = load_3mf(path.c_str(), config, ctxt, &model, false);
|
||||||
THEN("load should succeed") {
|
THEN("load should succeed") {
|
||||||
REQUIRE(ret);
|
REQUIRE(ret);
|
||||||
}
|
}
|
||||||
@ -56,7 +57,10 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
|
|||||||
// load back the model from the 3mf file
|
// load back the model from the 3mf file
|
||||||
Model dst_model;
|
Model dst_model;
|
||||||
DynamicPrintConfig dst_config;
|
DynamicPrintConfig dst_config;
|
||||||
load_3mf(test_file.c_str(), &dst_config, &dst_model, false);
|
{
|
||||||
|
ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable };
|
||||||
|
load_3mf(test_file.c_str(), dst_config, ctxt, &dst_model, false);
|
||||||
|
}
|
||||||
boost::filesystem::remove(test_file);
|
boost::filesystem::remove(test_file);
|
||||||
|
|
||||||
// compare meshes
|
// compare meshes
|
||||||
|
@ -12,7 +12,7 @@ SCENARIO("Generic config validation performs as expected.", "[Config]") {
|
|||||||
GIVEN("A config generated from default options") {
|
GIVEN("A config generated from default options") {
|
||||||
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||||
WHEN( "perimeter_extrusion_width is set to 250%, a valid value") {
|
WHEN( "perimeter_extrusion_width is set to 250%, a valid value") {
|
||||||
config.set_deserialize("perimeter_extrusion_width", "250%");
|
config.set_deserialize_strict("perimeter_extrusion_width", "250%");
|
||||||
THEN( "The config is read as valid.") {
|
THEN( "The config is read as valid.") {
|
||||||
REQUIRE(config.validate().empty());
|
REQUIRE(config.validate().empty());
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("A boolean option is set to a string value representing a 0 or 1") {
|
WHEN("A boolean option is set to a string value representing a 0 or 1") {
|
||||||
CHECK_NOTHROW(config.set_deserialize("gcode_comments", "1"));
|
CHECK_NOTHROW(config.set_deserialize_strict("gcode_comments", "1"));
|
||||||
THEN("The underlying value is set correctly.") {
|
THEN("The underlying value is set correctly.") {
|
||||||
REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == true);
|
REQUIRE(config.opt<ConfigOptionBool>("gcode_comments")->getBool() == true);
|
||||||
}
|
}
|
||||||
@ -62,7 +62,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("A numeric option is set from serialized string") {
|
WHEN("A numeric option is set from serialized string") {
|
||||||
config.set_deserialize("bed_temperature", "100");
|
config.set_deserialize_strict("bed_temperature", "100");
|
||||||
THEN("The underlying value is set correctly.") {
|
THEN("The underlying value is set correctly.") {
|
||||||
REQUIRE(config.opt<ConfigOptionInts>("bed_temperature")->get_at(0) == 100);
|
REQUIRE(config.opt<ConfigOptionInts>("bed_temperature")->get_at(0) == 100);
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") {
|
|||||||
}
|
}
|
||||||
WHEN("A numeric option is set to a non-numeric value.") {
|
WHEN("A numeric option is set to a non-numeric value.") {
|
||||||
THEN("A BadOptionTypeException exception is thown.") {
|
THEN("A BadOptionTypeException exception is thown.") {
|
||||||
REQUIRE_THROWS_AS(config.set_deserialize("perimeter_speed", "zzzz"), BadOptionTypeException);
|
REQUIRE_THROWS_AS(config.set_deserialize_strict("perimeter_speed", "zzzz"), BadOptionValueException);
|
||||||
}
|
}
|
||||||
THEN("The value does not change.") {
|
THEN("The value does not change.") {
|
||||||
REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 60.0);
|
REQUIRE(config.opt<ConfigOptionFloat>("perimeter_speed")->getFloat() == 60.0);
|
||||||
@ -120,7 +120,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("A float or percent is set as a percent through the string interface.") {
|
WHEN("A float or percent is set as a percent through the string interface.") {
|
||||||
config.set_deserialize("first_layer_extrusion_width", "100%");
|
config.set_deserialize_strict("first_layer_extrusion_width", "100%");
|
||||||
THEN("Value and percent flag are 100/true") {
|
THEN("Value and percent flag are 100/true") {
|
||||||
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
||||||
REQUIRE(tmp->percent == true);
|
REQUIRE(tmp->percent == true);
|
||||||
@ -128,7 +128,7 @@ SCENARIO("Config accessor functions perform as expected.", "[Config]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
WHEN("A float or percent is set as a float through the string interface.") {
|
WHEN("A float or percent is set as a float through the string interface.") {
|
||||||
config.set_deserialize("first_layer_extrusion_width", "100");
|
config.set_deserialize_strict("first_layer_extrusion_width", "100");
|
||||||
THEN("Value and percent flag are 100/false") {
|
THEN("Value and percent flag are 100/false") {
|
||||||
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
auto tmp = config.opt<ConfigOptionFloatOrPercent>("first_layer_extrusion_width");
|
||||||
REQUIRE(tmp->percent == false);
|
REQUIRE(tmp->percent == false);
|
||||||
@ -198,7 +198,7 @@ SCENARIO("Config ini load/save interface", "[Config]") {
|
|||||||
WHEN("new_from_ini is called") {
|
WHEN("new_from_ini is called") {
|
||||||
Slic3r::DynamicPrintConfig config;
|
Slic3r::DynamicPrintConfig config;
|
||||||
std::string path = std::string(TEST_DATA_DIR) + "/test_config/new_from_ini.ini";
|
std::string path = std::string(TEST_DATA_DIR) + "/test_config/new_from_ini.ini";
|
||||||
config.load_from_ini(path);
|
config.load_from_ini(path, ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
THEN("Config object contains ini file options.") {
|
THEN("Config object contains ini file options.") {
|
||||||
REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.size() == 1);
|
REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.size() == 1);
|
||||||
REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.front() == "#ABCD");
|
REQUIRE(config.option_throw<ConfigOptionStrings>("filament_colour", false)->values.front() == "#ABCD");
|
||||||
|
@ -10,7 +10,7 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
|
|||||||
auto config = DynamicPrintConfig::full_print_config();
|
auto config = DynamicPrintConfig::full_print_config();
|
||||||
|
|
||||||
// To test the "first_layer_extrusion_width" over "nozzle_diameter" chain.
|
// To test the "first_layer_extrusion_width" over "nozzle_diameter" chain.
|
||||||
config.set_deserialize( {
|
config.set_deserialize_strict( {
|
||||||
{ "printer_notes", " PRINTER_VENDOR_PRUSA3D PRINTER_MODEL_MK2 " },
|
{ "printer_notes", " PRINTER_VENDOR_PRUSA3D PRINTER_MODEL_MK2 " },
|
||||||
{ "nozzle_diameter", "0.6;0.6;0.6;0.6" },
|
{ "nozzle_diameter", "0.6;0.6;0.6;0.6" },
|
||||||
{ "temperature", "357;359;363;378" }
|
{ "temperature", "357;359;363;378" }
|
||||||
|
@ -282,7 +282,7 @@ bool ConfigBase__set(ConfigBase* THIS, const t_config_option_key &opt_key, SV* v
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if (! opt->deserialize(std::string(SvPV_nolen(value))))
|
if (! opt->deserialize(std::string(SvPV_nolen(value)), ForwardCompatibilitySubstitutionRule::Disable))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -295,7 +295,8 @@ bool ConfigBase__set_deserialize(ConfigBase* THIS, const t_config_option_key &op
|
|||||||
size_t len;
|
size_t len;
|
||||||
const char * c = SvPV(str, len);
|
const char * c = SvPV(str, len);
|
||||||
std::string value(c, len);
|
std::string value(c, len);
|
||||||
return THIS->set_deserialize_nothrow(opt_key, value);
|
ConfigSubstitutionContext ctxt{ ForwardCompatibilitySubstitutionRule::Disable };
|
||||||
|
return THIS->set_deserialize_nothrow(opt_key, value, ctxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigBase__set_ifndef(ConfigBase* THIS, const t_config_option_key &opt_key, SV* value, bool deserialize)
|
void ConfigBase__set_ifndef(ConfigBase* THIS, const t_config_option_key &opt_key, SV* value, bool deserialize)
|
||||||
|
@ -55,7 +55,7 @@
|
|||||||
%code%{
|
%code%{
|
||||||
auto config = new DynamicPrintConfig();
|
auto config = new DynamicPrintConfig();
|
||||||
try {
|
try {
|
||||||
config->load(path);
|
config->load(path, ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
RETVAL = config;
|
RETVAL = config;
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
delete config;
|
delete config;
|
||||||
@ -119,7 +119,7 @@
|
|||||||
%code%{
|
%code%{
|
||||||
auto config = new FullPrintConfig();
|
auto config = new FullPrintConfig();
|
||||||
try {
|
try {
|
||||||
config->load(path);
|
config->load(path, ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
RETVAL = static_cast<GCodeConfig*>(config);
|
RETVAL = static_cast<GCodeConfig*>(config);
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
delete config;
|
delete config;
|
||||||
|
@ -41,7 +41,7 @@ _new_from_width(CLASS, role, width, nozzle_diameter, height, bridge_flow_ratio)
|
|||||||
float bridge_flow_ratio;
|
float bridge_flow_ratio;
|
||||||
CODE:
|
CODE:
|
||||||
ConfigOptionFloatOrPercent optwidth;
|
ConfigOptionFloatOrPercent optwidth;
|
||||||
optwidth.deserialize(width);
|
optwidth.deserialize(width, ForwardCompatibilitySubstitutionRule::Disable);
|
||||||
RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height, bridge_flow_ratio));
|
RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height, bridge_flow_ratio));
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
RETVAL
|
RETVAL
|
||||||
|
@ -18,8 +18,6 @@
|
|||||||
%code%{ RETVAL = &THIS->thin_fills; %};
|
%code%{ RETVAL = &THIS->thin_fills; %};
|
||||||
Ref<SurfaceCollection> fill_surfaces()
|
Ref<SurfaceCollection> fill_surfaces()
|
||||||
%code%{ RETVAL = &THIS->fill_surfaces; %};
|
%code%{ RETVAL = &THIS->fill_surfaces; %};
|
||||||
Polygons bridged()
|
|
||||||
%code%{ RETVAL = THIS->bridged; %};
|
|
||||||
Ref<ExtrusionEntityCollection> perimeters()
|
Ref<ExtrusionEntityCollection> perimeters()
|
||||||
%code%{ RETVAL = &THIS->perimeters; %};
|
%code%{ RETVAL = &THIS->perimeters; %};
|
||||||
Ref<ExtrusionEntityCollection> fills()
|
Ref<ExtrusionEntityCollection> fills()
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
%name{read_from_file} Model(std::string input_file, bool add_default_instances = true)
|
%name{read_from_file} Model(std::string input_file, bool add_default_instances = true)
|
||||||
%code%{
|
%code%{
|
||||||
try {
|
try {
|
||||||
RETVAL = new Model(Model::read_from_file(input_file, nullptr, add_default_instances));
|
RETVAL = new Model(Model::read_from_file(input_file, nullptr, nullptr, only_if(add_default_instances, Model::LoadAttribute::AddDefaultInstances)));
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
croak("Error while opening %s: %s\n", input_file.c_str(), e.what());
|
croak("Error while opening %s: %s\n", input_file.c_str(), e.what());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user