Merge pull request #50 from Prusa-Development/ys_thumbnails_refact

Thumbnails refactoring
This commit is contained in:
Oleksandra Yushchenko 2023-09-13 15:29:20 +02:00 committed by GitHub
commit c6ce4a662e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 595 additions and 134 deletions

View File

@ -874,8 +874,15 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
// Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format".
// If "thumbnails_format" is not defined, export to PNG. // If "thumbnails_format" is not defined, export to PNG.
if (std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config()); auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config());
! thumbnails.empty())
if (errors != enum_bitmask<ThumbnailError>()) {
std::string error_str = format("Invalid thumbnails value:");
error_str += GCodeThumbnails::get_error_string(errors);
throw Slic3r::ExportError(error_str);
}
if (!thumbnails.empty())
GCodeThumbnails::generate_binary_thumbnails( GCodeThumbnails::generate_binary_thumbnails(
thumbnail_cb, binary_data.thumbnails, thumbnails, thumbnail_cb, binary_data.thumbnails, thumbnails,
[&print]() { print.throw_if_canceled(); }); [&print]() { print.throw_if_canceled(); });
@ -1009,8 +1016,15 @@ void GCodeGenerator::_do_export(Print& print, GCodeOutputStream &file, Thumbnail
if (! export_to_binary_gcode) { if (! export_to_binary_gcode) {
// if exporting gcode in ascii format, generate the thumbnails here // if exporting gcode in ascii format, generate the thumbnails here
if (std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> thumbnails = GCodeThumbnails::make_thumbnail_list(print.full_print_config()); auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config());
! thumbnails.empty())
if (errors != enum_bitmask<ThumbnailError>()) {
std::string error_str = format("Invalid thumbnails value:");
error_str += GCodeThumbnails::get_error_string(errors);
throw Slic3r::ExportError(error_str);
}
if (!thumbnails.empty())
GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails, GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, thumbnails,
[&file](const char* sz) { file.write(sz); }, [&file](const char* sz) { file.write(sz); },
[&print]() { print.throw_if_canceled(); }); [&print]() { print.throw_if_canceled(); });

View File

@ -9,6 +9,9 @@
#include <jpeglib.h> #include <jpeglib.h>
#include <jerror.h> #include <jerror.h>
#include <boost/algorithm/string.hpp>
#include <string>
namespace Slic3r::GCodeThumbnails { namespace Slic3r::GCodeThumbnails {
using namespace std::literals; using namespace std::literals;
@ -120,38 +123,81 @@ std::unique_ptr<CompressedImageBuffer> compress_thumbnail(const ThumbnailData &d
} }
} }
std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> make_thumbnail_list(const DynamicPrintConfig &config) std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext /*= "PNG"sv*/)
{
if (thumbnails_string.empty())
return {};
std::istringstream is(thumbnails_string);
std::string point_str;
ThumbnailErrors errors;
// generate thumbnails data to process it
GCodeThumbnailDefinitionsList thumbnails_list;
while (std::getline(is, point_str, ',')) {
Vec2d point(Vec2d::Zero());
GCodeThumbnailsFormat format;
std::istringstream iss(point_str);
std::string coord_str;
if (std::getline(iss, coord_str, 'x') && !coord_str.empty()) {
std::istringstream(coord_str) >> point(0);
if (std::getline(iss, coord_str, '/') && !coord_str.empty()) {
std::istringstream(coord_str) >> point(1);
if (0 < point(0) && point(0) < 1000 && 0 < point(1) && point(1) < 1000) {
std::string ext_str;
std::getline(iss, ext_str, '/');
if (ext_str.empty())
ext_str = def_ext.empty() ? "PNG"sv : def_ext;
// check validity of extention
boost::to_upper(ext_str);
if (!ConfigOptionEnum<GCodeThumbnailsFormat>::from_string(ext_str, format)) {
format = GCodeThumbnailsFormat::PNG;
errors = enum_bitmask(errors | ThumbnailError::InvalidExt);
}
thumbnails_list.emplace_back(std::make_pair(format, point));
}
else
errors = enum_bitmask(errors | ThumbnailError::OutOfRange);
continue;
}
}
errors = enum_bitmask(errors | ThumbnailError::InvalidVal);
}
return std::make_pair(std::move(thumbnails_list), errors);
}
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const ConfigBase& config)
{ {
// ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // ??? Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format".
// ??? If "thumbnails_format" is not defined, export to PNG. // ??? If "thumbnails_format" is not defined, export to PNG.
// generate thumbnails data to process it // generate thumbnails data to process it
std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> thumbnails_list; if (const auto thumbnails_value = config.option<ConfigOptionString>("thumbnails"))
if (const auto thumbnails_value = config.option<ConfigOptionString>("thumbnails")) { return make_and_check_thumbnail_list(thumbnails_value->value);
std::string str = thumbnails_value->value;
std::istringstream is(str);
std::string point_str;
while (std::getline(is, point_str, ',')) {
Vec2d point(Vec2d::Zero());
GCodeThumbnailsFormat format;
std::istringstream iss(point_str);
std::string coord_str;
if (std::getline(iss, coord_str, 'x')) {
std::istringstream(coord_str) >> point(0);
if (std::getline(iss, coord_str, '/')) {
std::istringstream(coord_str) >> point(1);
std::string ext_str;
if (std::getline(iss, ext_str, '/'))
format = ext_str == "JPG" ? GCodeThumbnailsFormat::JPG :
ext_str == "QOI" ? GCodeThumbnailsFormat::QOI :GCodeThumbnailsFormat::PNG;
}
}
thumbnails_list.emplace_back(std::make_pair(format, point));
}
}
return thumbnails_list; return {};
}
std::string get_error_string(const ThumbnailErrors& errors)
{
std::string error_str;
if (errors.has(ThumbnailError::InvalidVal))
error_str += "\n - " + format("Invalid input format. Expected vector of dimensions in the following format: \"%1%\"", "XxYxEXT, XxYxEXT, ...");
if (errors.has(ThumbnailError::OutOfRange))
error_str += "\n - Input value is out of range";
if (errors.has(ThumbnailError::InvalidExt))
error_str += "\n - Some input extention is invalid";
return error_str;
} }
} // namespace Slic3r::GCodeThumbnails } // namespace Slic3r::GCodeThumbnails

View File

@ -17,6 +17,14 @@
#include <boost/beast/core/detail/base64.hpp> #include <boost/beast/core/detail/base64.hpp>
#include "../libslic3r/enum_bitmask.hpp"
namespace Slic3r {
enum class ThumbnailError : int { InvalidVal, OutOfRange, InvalidExt };
using ThumbnailErrors = enum_bitmask<ThumbnailError>;
ENABLE_ENUM_BITMASK_OPERATORS(ThumbnailError);
}
namespace Slic3r::GCodeThumbnails { namespace Slic3r::GCodeThumbnails {
struct CompressedImageBuffer struct CompressedImageBuffer
@ -29,7 +37,13 @@ struct CompressedImageBuffer
std::unique_ptr<CompressedImageBuffer> compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format); std::unique_ptr<CompressedImageBuffer> compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format);
std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> make_thumbnail_list(const DynamicPrintConfig &config); typedef std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>> GCodeThumbnailDefinitionsList;
using namespace std::literals;
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const std::string& thumbnails_string, const std::string_view def_ext = "PNG"sv);
std::pair<GCodeThumbnailDefinitionsList, ThumbnailErrors> make_and_check_thumbnail_list(const ConfigBase &config);
std::string get_error_string(const ThumbnailErrors& errors);
template<typename WriteToOutput, typename ThrowIfCanceledCallback> template<typename WriteToOutput, typename ThrowIfCanceledCallback>
inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>>& thumbnails_list, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>>& thumbnails_list, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled)

View File

@ -44,6 +44,7 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "Utils.hpp" #include "Utils.hpp"
#include "PlaceholderParser.hpp" #include "PlaceholderParser.hpp"
#include "GCode/Thumbnails.hpp"
using boost::property_tree::ptree; using boost::property_tree::ptree;
@ -1299,7 +1300,6 @@ static const std::set<std::string> independent_from_extruder_number_options = {
"filament_ramming_parameters", "filament_ramming_parameters",
"gcode_substitutions", "gcode_substitutions",
"post_process", "post_process",
"thumbnails",
}; };
bool PresetCollection::is_independent_from_extruder_number_option(const std::string& opt_key) bool PresetCollection::is_independent_from_extruder_number_option(const std::string& opt_key)
@ -1323,6 +1323,15 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
} else if (opt_key == "default_filament_profile") { } else if (opt_key == "default_filament_profile") {
// Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help. // Ignore this field, it is not presented to the user, therefore showing a "modified" flag for this parameter does not help.
// Also the length of this field may differ, which may lead to a crash if the block below is used. // Also the length of this field may differ, which may lead to a crash if the block below is used.
} else if (opt_key == "thumbnails") {
// "thumbnails" can not containes a extentions in old config but are valid and use PNG extention by default
// So, check if "thumbnails" is really changed
// We will compare full thumnails instead of exactly config values
auto [thumbnails, er] = GCodeThumbnails::make_and_check_thumbnail_list(config_this);
auto [thumbnails_new, er_new] = GCodeThumbnails::make_and_check_thumbnail_list(config_other);
if (thumbnails != thumbnails_new || er != er_new)
// if those strings are actually the same, erase them from the list of dirty oprions
diff.emplace_back(opt_key);
} else { } else {
switch (other_opt->type()) { switch (other_opt->type()) {
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break; case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
@ -1346,7 +1355,14 @@ bool PresetCollection::is_dirty(const Preset *edited, const Preset *reference)
{ {
if (edited != nullptr && reference != nullptr) { if (edited != nullptr && reference != nullptr) {
// Only compares options existing in both configs. // Only compares options existing in both configs.
if (! reference->config.equals(edited->config)) bool is_dirty = !reference->config.equals(edited->config);
if (is_dirty && edited->type != Preset::TYPE_FILAMENT) {
// for non-filaments preset check deep difference for compared configs
// there can be cases (as for thumbnails), when configs can logically equal
// even when their values are not equal.
is_dirty = !deep_diff(edited->config, reference->config).empty();
}
if (is_dirty)
return true; return true;
// The "compatible_printers" option key is handled differently from the others: // The "compatible_printers" option key is handled differently from the others:
// It is not mandatory. If the key is missing, it means it is compatible with any printer. // It is not mandatory. If the key is missing, it means it is compatible with any printer.

View File

@ -24,6 +24,7 @@
#include "I18N.hpp" #include "I18N.hpp"
#include "SLA/SupportTree.hpp" #include "SLA/SupportTree.hpp"
#include "GCode/Thumbnails.hpp"
#include <set> #include <set>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
@ -4371,6 +4372,35 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
// Don't convert single options here, implement such conversion in PrintConfigDef::handle_legacy() instead. // Don't convert single options here, implement such conversion in PrintConfigDef::handle_legacy() instead.
void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config) void PrintConfigDef::handle_legacy_composite(DynamicPrintConfig &config)
{ {
if (config.has("thumbnails")) {
std::string extention;
if (config.has("thumbnails_format")) {
if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format")) {
auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt());
if (label.has_value())
extention = *label;
}
}
std::string thumbnails_str = config.opt_string("thumbnails");
auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(thumbnails_str, extention);
if (errors != enum_bitmask<ThumbnailError>()) {
std::string error_str = "\n" + format("Invalid value provided for parameter %1%: %2%", "thumbnails", thumbnails_str);
error_str += GCodeThumbnails::get_error_string(errors);
throw BadOptionValueException(error_str);
}
if (!thumbnails_list.empty()) {
const auto& extentions = ConfigOptionEnum<GCodeThumbnailsFormat>::get_enum_names();
thumbnails_str.clear();
for (const auto& [ext, size] : thumbnails_list)
thumbnails_str += format("%1%x%2%/%3%, ", size.x(), size.y(), extentions[int(ext)]);
thumbnails_str.resize(thumbnails_str.length() - 2);
config.set_key_value("thumbnails", new ConfigOptionString(thumbnails_str));
}
}
} }
const PrintConfigDef print_config_def; const PrintConfigDef print_config_def;

View File

@ -15,6 +15,7 @@
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
#include "libslic3r/enum_bitmask.hpp" #include "libslic3r/enum_bitmask.hpp"
#include "libslic3r/GCode/Thumbnails.hpp"
#include <regex> #include <regex>
#include <wx/numformatter.h> #include <wx/numformatter.h>
@ -33,13 +34,7 @@
#define wxOSX false #define wxOSX false
#endif #endif
namespace Slic3r { namespace Slic3r :: GUI {
enum class ThumbnailError : int { InvalidVal, OutOfRange, InvalidExt };
using ThumbnailErrors = enum_bitmask<ThumbnailError>;
ENABLE_ENUM_BITMASK_OPERATORS(ThumbnailError);
namespace GUI {
wxString double_to_string(double const value, const int max_precision /*= 4*/) wxString double_to_string(double const value, const int max_precision /*= 4*/)
{ {
@ -71,82 +66,23 @@ wxString double_to_string(double const value, const int max_precision /*= 4*/)
return s; return s;
} }
bool is_valid_thumbnails_extention(wxString& ext)
{
ext.UpperCase();
static const std::vector<wxString> extentions = { "PNG", "JPG", "QOI" };
return std::find(extentions.begin(), extentions.end(), ext) != extentions.end();
}
ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ext = "PNG") ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ext = "PNG")
{ {
bool invalid_val, out_of_range_val, invalid_ext; std::string input_string = into_u8(str);
invalid_val = out_of_range_val = invalid_ext = false;
str.Replace(" ", wxEmptyString, true);
if (!str.IsEmpty()) { str.Clear();
std::vector<std::pair<Vec2d, std::string>> out_thumbnails; auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(input_string);
if (!thumbnails_list.empty()) {
wxStringTokenizer thumbnails(str, ","); const auto& extentions = ConfigOptionEnum<GCodeThumbnailsFormat>::get_enum_names();
while (thumbnails.HasMoreTokens()) { for (const auto& [format, size] : thumbnails_list)
wxString token = thumbnails.GetNextToken(); str += format_wxstr("%1%x%2%/%3%, ", size.x(), size.y(), extentions[int(format)]);
double x, y; str.resize(str.Len() - 2);
wxStringTokenizer thumbnail(token, "x");
if (thumbnail.HasMoreTokens()) {
wxString x_str = thumbnail.GetNextToken();
if (x_str.ToDouble(&x) && thumbnail.HasMoreTokens()) {
wxStringTokenizer y_and_ext(thumbnail.GetNextToken(), "/");
wxString y_str = y_and_ext.GetNextToken();
if (y_str.ToDouble(&y)) {
// thumbnail has no extension
if (0 < x && x < 1000 && 0 < y && y < 1000) {
wxString ext = y_and_ext.HasMoreTokens() ? y_and_ext.GetNextToken() : def_ext;
bool is_valid_ext = is_valid_thumbnails_extention(ext);
invalid_ext |= !is_valid_ext;
out_thumbnails.push_back({ Vec2d(x, y), into_u8(is_valid_ext ? ext : def_ext) });
continue;
}
out_of_range_val |= true;
continue;
}
}
}
invalid_val |= true;
}
str.Clear();
for (const auto& [size, ext] : out_thumbnails)
str += format_wxstr("%1%x%2%/%3%, ", size.x(), size.y(), ext);
str.resize(str.Len()-2);
} }
ThumbnailErrors errors = only_if(invalid_val, ThumbnailError::InvalidVal) |
only_if(invalid_ext, ThumbnailError::InvalidExt) |
only_if(out_of_range_val, ThumbnailError::OutOfRange);
return errors; return errors;
} }
wxString get_valid_thumbnails_string(const DynamicPrintConfig& config)
{
// >>> ysFIXME - temporary code, till "thumbnails_format" options exists in config
wxString format = "PNG";
if (const ConfigOptionDef* opt = config.def()->get("thumbnails_format"))
if (auto label = opt->enum_def->enum_to_label(config.option("thumbnails_format")->getInt());
label.has_value())
format = from_u8(*label);
// <<<
wxString str = from_u8(config.opt_string("thumbnails"));
validate_thumbnails_string(str, format);
return str;
}
Field::~Field() Field::~Field()
{ {
if (m_on_kill_focus) if (m_on_kill_focus)
@ -1765,5 +1701,5 @@ boost::any& SliderCtrl::get_value()
} }
} // GUI } // Slic3r :: GUI
} // Slic3r

View File

@ -41,7 +41,6 @@ using t_change = std::function<void(const t_config_option_key&, const boost::any
using t_back_to_init = std::function<void(const std::string&)>; using t_back_to_init = std::function<void(const std::string&)>;
wxString double_to_string(double const value, const int max_precision = 4); wxString double_to_string(double const value, const int max_precision = 4);
wxString get_valid_thumbnails_string(const DynamicPrintConfig& config);
class UndoValueUIManager class UndoValueUIManager
{ {

View File

@ -925,10 +925,7 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
} }
break; break;
case coString: { case coString: {
if (opt_key == "thumbnails") ret = from_u8(config.opt_string(opt_key));
ret = get_valid_thumbnails_string(config);
else
ret = from_u8(config.opt_string(opt_key));
break; break;
} }
case coStrings: case coStrings:

View File

@ -43,8 +43,6 @@ namespace Slic3r {
class BuildVolume; class BuildVolume;
class Model; class Model;
class ModelObject; class ModelObject;
enum class ModelObjectCutAttribute : int;
using ModelObjectCutAttributes = enum_bitmask<ModelObjectCutAttribute>;
class ModelInstance; class ModelInstance;
class Print; class Print;
class SLAPrint; class SLAPrint;

View File

@ -27,6 +27,7 @@
#include "libslic3r/Model.hpp" #include "libslic3r/Model.hpp"
#include "libslic3r/GCode/GCodeProcessor.hpp" #include "libslic3r/GCode/GCodeProcessor.hpp"
#include "libslic3r/GCode/GCodeWriter.hpp" #include "libslic3r/GCode/GCodeWriter.hpp"
#include "libslic3r/GCode/Thumbnails.hpp"
#include "slic3r/Utils/Http.hpp" #include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/PrintHost.hpp" #include "slic3r/Utils/PrintHost.hpp"
@ -662,21 +663,6 @@ void Tab::update_changed_ui()
if (tab->m_sys_extruders_count != tab->m_extruders_count) if (tab->m_sys_extruders_count != tab->m_extruders_count)
nonsys_options.emplace_back("extruders_count"); nonsys_options.emplace_back("extruders_count");
} }
// "thumbnails" can not containe a extentions in old config but are valid and use PNG extention by default
// So, check if "thumbnails" is really changed
// We will compare full strings for thumnails instead of exactly config values
{
auto check_thumbnails_option = [](std::vector<std::string>& keys, const DynamicPrintConfig& config, const DynamicPrintConfig& config_new) {
if (auto it = std::find(keys.begin(), keys.end(), "thumbnails"); it != keys.end())
if (get_valid_thumbnails_string(config) == get_valid_thumbnails_string(config_new))
// if those strings are actually the same, erase them from the list of dirty oprions
keys.erase(it);
};
check_thumbnails_option(dirty_options, m_presets->get_edited_preset().config, m_presets->get_selected_preset().config);
if (const Preset* parent_preset = m_presets->get_selected_preset_parent())
check_thumbnails_option(nonsys_options, m_presets->get_edited_preset().config, parent_preset->config);
}
} }
for (auto& it : m_options_list) for (auto& it : m_options_list)
@ -2673,6 +2659,32 @@ void TabPrinter::build_fff()
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) { optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
wxTheApp->CallAfter([this, opt_key, value]() { wxTheApp->CallAfter([this, opt_key, value]() {
if (opt_key == "thumbnails" && m_config->has("thumbnails_format")) {
// to backward compatibility we need to update "thumbnails_format" from new "thumbnails"
if (const std::string val = boost::any_cast<std::string>(value); !value.empty()) {
auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(val);
if (errors != enum_bitmask<ThumbnailError>()) {
std::string error_str = format(_u8L("Invalid value provided for parameter %1%: %2%"), "thumbnails", val);
error_str += GCodeThumbnails::get_error_string(errors);
InfoDialog(parent(), _L("G-code flavor is switched"), from_u8(error_str)).ShowModal();
}
if (!thumbnails_list.empty()) {
GCodeThumbnailsFormat old_format = GCodeThumbnailsFormat(m_config->option("thumbnails_format")->getInt());
GCodeThumbnailsFormat new_format = thumbnails_list.begin()->first;
if (old_format != new_format) {
DynamicPrintConfig new_conf = *m_config;
auto* opt = m_config->option("thumbnails_format")->clone();
opt->setInt(int(new_format));
new_conf.set_key_value("thumbnails_format", opt);
load_config(new_conf);
}
}
}
}
if (opt_key == "silent_mode") { if (opt_key == "silent_mode") {
bool val = boost::any_cast<bool>(value); bool val = boost::any_cast<bool>(value);
if (m_use_silent_mode != val) { if (m_use_silent_mode != val) {

View File

@ -1160,11 +1160,8 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
} }
return _L("Undef"); return _L("Undef");
} }
case coString: { case coString:
if (opt_key == "thumbnails")
return get_valid_thumbnails_string(config);
return from_u8(config.opt_string(opt_key)); return from_u8(config.opt_string(opt_key));
}
case coStrings: { case coStrings: {
const ConfigOptionStrings* strings = config.opt<ConfigOptionStrings>(opt_key); const ConfigOptionStrings* strings = config.opt<ConfigOptionStrings>(opt_key);
if (strings) { if (strings) {

View File

@ -28,6 +28,7 @@ endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
add_subdirectory(arrange) add_subdirectory(arrange)
add_subdirectory(thumbnails)
add_subdirectory(libslic3r) add_subdirectory(libslic3r)
add_subdirectory(slic3rutils) add_subdirectory(slic3rutils)
add_subdirectory(fff_print) add_subdirectory(fff_print)

View File

@ -0,0 +1,13 @@
get_filename_component(_TEST_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
add_executable(${_TEST_NAME}_tests
${_TEST_NAME}_tests_main.cpp
test_thumbnails_input_string.cpp
test_thumbnails_ini_string.cpp
)
target_link_libraries(${_TEST_NAME}_tests test_common libslic3r)
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS})

View File

@ -0,0 +1,235 @@
#include <catch2/catch.hpp>
#include "libslic3r/Config.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <libslic3r/GCode/Thumbnails.hpp>
using namespace Slic3r;
using namespace GCodeThumbnails;
static std::string empty_thumbnails()
{
return "thumbnails = \n"
"thumbnails_format = ";
}
static std::string valid_thumbnails()
{
return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG\n"
"thumbnails_format = JPG";
}
static std::string valid_thumbnails2()
{
return "thumbnails = 160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG\n"
"thumbnails_format = pnG";
}
static std::string valid_thumbnails3()
{
return "thumbnails = 160x120/JPG, 23x78/QOI, 230x780/JPG";
}
static std::string old_valid_thumbnails()
{
return "thumbnails = 160x120\n"
"thumbnails_format = JPG";
}
static std::string old_valid_thumbnails2()
{
return "thumbnails = 160x120, 23x78, 320x240\n"
"thumbnails_format = PNG";
}
static std::string old_invalid_thumbnails()
{
return "thumbnails = 160x\n"
"thumbnails_format = JPG";
}
static std::string old_invalid_thumbnails2()
{
return "thumbnails = 160x120, 23*78, 320x240\n"
"thumbnails_format = PNG";
}
static std::string out_of_range_thumbnails()
{
return "thumbnails = 1160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
"thumbnails_format = PNG";
}
static std::string out_of_range_thumbnails2()
{
return "thumbnails = 1160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG\n"
"thumbnails_format = PNG";
}
static std::string invalid_ext_thumbnails()
{
return "thumbnails = 1160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
"thumbnails_format = QOI";
}
static std::string invalid_ext_thumbnails2()
{
return "thumbnails = 1160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG\n"
"thumbnails_format = PNG";
}
static std::string invalid_val_thumbnails()
{
return "thumbnails = 1160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
"thumbnails_format = JPG";
}
static std::string invalid_val_thumbnails2()
{
return "thumbnails = x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
"thumbnails_format = PNG";
}
static std::string invalid_val_thumbnails3()
{
return "thumbnails = 1x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG\n"
"thumbnails_format = qoi";
}
static std::string invalid_val_thumbnails4()
{
return "thumbnails = 123*78/QOI, 320x240/PNG, 230x780/JPG\n"
"thumbnails_format = jpG";
}
static DynamicPrintConfig thumbnails_config()
{
DynamicPrintConfig config;
config.apply_only(FullPrintConfig::defaults() , { "thumbnails", "thumbnails_format" });
return config;
}
TEST_CASE("Validate Empty Thumbnails", "[Thumbnails in Config]") {
DynamicPrintConfig config = thumbnails_config();
auto test_loaded_config = [](DynamicPrintConfig& config) {
REQUIRE(config.opt<ConfigOptionString>("thumbnails")->empty());
REQUIRE(config.option("thumbnails_format")->getInt() == (int)GCodeThumbnailsFormat::PNG);
};
SECTION("Load empty init_data") {
REQUIRE_NOTHROW(config.load_from_ini_string("", Enable));
test_loaded_config(config);
}
SECTION("Load empty format and empty thumbnails") {
REQUIRE_THROWS_AS(config.load_from_ini_string(empty_thumbnails(), Enable), BadOptionValueException);
test_loaded_config(config);
}
}
TEST_CASE("Validate New Thumbnails", "[Thumbnails in Config]") {
DynamicPrintConfig config = thumbnails_config();
auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) {
REQUIRE(!config.opt<ConfigOptionString>("thumbnails")->empty());
REQUIRE(config.option("thumbnails_format")->getInt() == (int)format);
};
SECTION("Test 1 (valid)") {
REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails(), Enable));
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
}
SECTION("Test 2 (valid)") {
REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails2(), Enable));
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 3 (valid)") {
REQUIRE_NOTHROW(config.load_from_ini_string(valid_thumbnails3(), Enable));
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 1 (out_of_range)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 2 (out_of_range)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(out_of_range_thumbnails2(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 1 (invalid_ext)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::QOI);
}
SECTION("Test 2 (invalid_ext)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_ext_thumbnails2(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 1 (invalid_val)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
}
SECTION("Test 2 (invalid_val)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails2(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 3 (invalid_val)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails3(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 4 (invalid_val)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(invalid_val_thumbnails4(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
}
TEST_CASE("Validate Old Thumbnails", "[Thumbnails in Config]") {
DynamicPrintConfig config = thumbnails_config();
auto test_loaded_config = [](DynamicPrintConfig& config, GCodeThumbnailsFormat format) {
REQUIRE(!config.opt<ConfigOptionString>("thumbnails")->empty());
REQUIRE(config.option("thumbnails_format")->getInt() == (int)format);
};
SECTION("Test 1 (valid)") {
REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails(), Enable));
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
}
SECTION("Test 2 (valid)") {
REQUIRE_NOTHROW(config.load_from_ini_string(old_valid_thumbnails2(), Enable));
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
SECTION("Test 1 (invalid)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::JPG);
}
SECTION("Test 2 (invalid)") {
REQUIRE_THROWS_AS(config.load_from_ini_string(old_invalid_thumbnails2(), Enable), BadOptionValueException);
test_loaded_config(config, GCodeThumbnailsFormat::PNG);
}
}

View File

@ -0,0 +1,152 @@
#include <catch2/catch.hpp>
#include <test_utils.hpp>
#include <libslic3r/GCode/Thumbnails.hpp>
using namespace Slic3r;
using namespace GCodeThumbnails;
// Test Thumbnails lines
static std::string empty_thumbnails()
{
return "";
}
static std::string valid_thumbnails()
{
return "160x120/PNG, 23x78/QOI, 230x780/JPG";
}
static std::string valid_thumbnails2()
{
return "160x120/PNG, 23x78/QOi, 320x240/PNg, 230x780/JPG";
}
static std::string out_of_range_thumbnail()
{
return "160x1200/PNG, 23x78/QOI, 320x240/PNG, 230x780/JPG";
}
static std::string out_of_range_thumbnail2()
{
return "160x120/PNG, 23x78/QOI, -320x240/PNG, 230x780/JPG";
}
static std::string invalid_ext_thumbnail()
{
return "160x120/PNk, 23x78/QOI, 320x240/PNG, 230x780/JPG";
}
static std::string invalid_ext_thumbnail2()
{
return "160x120/PNG, 23x78/QO, 320x240/PNG, 230x780/JPG";
}
static std::string invalid_val_thumbnail()
{
return "160x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG";
}
static std::string invalid_val_thumbnail2()
{
return "x120/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG";
}
static std::string invalid_val_thumbnail3()
{
return "x/PNg, 23x78/QOI, 320x240/PNG, 230x780/JPG";
}
static std::string invalid_val_thumbnail4()
{
return "23*78/QOI, 320x240/PNG, 230x780/JPG";
}
TEST_CASE("Empty Thumbnails", "[Thumbnails]") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(empty_thumbnails());
REQUIRE(errors == enum_bitmask<ThumbnailError>());
REQUIRE(thumbnails.empty());
}
TEST_CASE("Valid Thumbnails", "[Thumbnails]") {
SECTION("Test 1") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails());
REQUIRE(errors == enum_bitmask<ThumbnailError>());
REQUIRE(thumbnails.size() == 3);
}
SECTION("Test 2") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(valid_thumbnails2());
REQUIRE(errors == enum_bitmask<ThumbnailError>());
REQUIRE(thumbnails.size() == 4);
}
}
TEST_CASE("Out of range Thumbnails", "[Thumbnails]") {
SECTION("Test 1") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::OutOfRange));
REQUIRE(thumbnails.size() == 3);
}
SECTION("Test 2") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(out_of_range_thumbnail2());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::OutOfRange));
REQUIRE(thumbnails.size() == 3);
}
}
TEST_CASE("Invalid extention Thumbnails", "[Thumbnails]") {
SECTION("Test 1") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::InvalidExt));
REQUIRE(thumbnails.size() == 4);
}
SECTION("Test 2") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_ext_thumbnail2());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::InvalidExt));
REQUIRE(thumbnails.size() == 4);
}
}
TEST_CASE("Invalid value Thumbnails", "[Thumbnails]") {
SECTION("Test 1") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::InvalidVal));
REQUIRE(thumbnails.size() == 3);
}
SECTION("Test 2") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail2());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::InvalidVal));
REQUIRE(thumbnails.size() == 3);
}
SECTION("Test 3") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail3());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::InvalidVal));
REQUIRE(thumbnails.size() == 3);
}
SECTION("Test 4") {
auto [thumbnails, errors] = make_and_check_thumbnail_list(invalid_val_thumbnail4());
REQUIRE(errors != enum_bitmask<ThumbnailError>());
REQUIRE(errors.has(ThumbnailError::InvalidVal));
REQUIRE(thumbnails.size() == 2);
}
}

View File

@ -0,0 +1 @@
#include <catch_main.hpp>