mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-07 23:17:35 -06:00
Thumbnails feature revamp. (#5555)
* Thumbnails feature revamp. Support generating different size/format combinations * misc fix Co-authored-by: Lukas Matena <lukasmatena@seznam.cz>
This commit is contained in:
parent
50d00a1d54
commit
c083541e0a
26 changed files with 1145 additions and 854 deletions
|
@ -137,8 +137,9 @@
|
|||
"nozzle_type": "undefine",
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"320x320",
|
||||
"160x160"
|
||||
"320x320/PNG",
|
||||
"32x32/COLPIC",
|
||||
"160x160/COLPIC"
|
||||
],
|
||||
"thumbnails_format": "ColPic"
|
||||
"thumbnails_format": "PNG"
|
||||
}
|
||||
|
|
|
@ -86,10 +86,15 @@
|
|||
"machine_pause_gcode": "M601",
|
||||
"machine_start_gcode": "M862.3 P \"[printer_model]\" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow",
|
||||
"printer_notes": "Don't remove the following keywords! These keywords are used in the \"compatible printer\" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n",
|
||||
|
||||
"scan_first_layer": "0",
|
||||
"machine_load_filament_time": "17",
|
||||
"machine_unload_filament_time": "16",
|
||||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0"
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -80,10 +80,15 @@
|
|||
"machine_pause_gcode": "M601",
|
||||
"machine_start_gcode": "M862.3 P \"[printer_model]\" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow",
|
||||
"printer_notes": "Don't remove the following keywords! These keywords are used in the \"compatible printer\" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n",
|
||||
|
||||
"scan_first_layer": "0",
|
||||
"machine_load_filament_time": "17",
|
||||
"machine_unload_filament_time": "16",
|
||||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0"
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -80,10 +80,15 @@
|
|||
"machine_pause_gcode": "M601",
|
||||
"machine_start_gcode": "M862.3 P \"[printer_model]\" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow",
|
||||
"printer_notes": "Don't remove the following keywords! These keywords are used in the \"compatible printer\" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n",
|
||||
|
||||
"scan_first_layer": "0",
|
||||
"machine_load_filament_time": "17",
|
||||
"machine_unload_filament_time": "16",
|
||||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0"
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -80,10 +80,15 @@
|
|||
"machine_pause_gcode": "M601",
|
||||
"machine_start_gcode": "M862.3 P \"[printer_model]\" ; printer model check\nG90 ; use absolute coordinates\nM83 ; extruder relative mode\nM104 S170 ; set extruder temp for bed leveling\nM140 S[first_layer_bed_temperature] ; set bed temp\nM109 R170 ; wait for bed leveling temp\nM190 S[first_layer_bed_temperature] ; wait for bed temp\nM204 T1250 ; set travel acceleration\nG28 ; home all without mesh bed level\nG29 ; mesh bed leveling \nM204 T[machine_max_acceleration_travel] ; restore travel acceleration\nM104 S[first_layer_temperature] ; set extruder temp\nG92 E0\nG1 Y-2 X179 F2400\nG1 Z3 F720\nM109 S[first_layer_temperature] ; wait for extruder temp\n\n; intro line\nG1 X170 F1000\nG1 Z0.2 F720\nG1 X110 E8 F900\nG1 X40 E10 F700\nG92 E0\n\nM221 S95 ; set flow",
|
||||
"printer_notes": "Don't remove the following keywords! These keywords are used in the \"compatible printer\" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_MINI\n",
|
||||
|
||||
"scan_first_layer": "0",
|
||||
"machine_load_filament_time": "17",
|
||||
"machine_unload_filament_time": "16",
|
||||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0"
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -26,5 +26,11 @@
|
|||
],
|
||||
"retraction_minimum_travel": [
|
||||
"1.0"
|
||||
],
|
||||
"thumbnails": [
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -110,8 +110,9 @@
|
|||
"nozzle_type": "brass",
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16",
|
||||
"313x173",
|
||||
"440x240"
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -26,5 +26,11 @@
|
|||
],
|
||||
"retraction_speed": [
|
||||
"70"
|
||||
]
|
||||
],
|
||||
"thumbnails": [
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -29,5 +29,11 @@
|
|||
],
|
||||
"deretraction_speed": [
|
||||
"20"
|
||||
]
|
||||
],
|
||||
"thumbnails": [
|
||||
"16x16/QOI",
|
||||
"220x124/QOI",
|
||||
"200x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -73,8 +73,10 @@
|
|||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16",
|
||||
"313x173",
|
||||
"440x240"
|
||||
"16x16/QOI",
|
||||
"313x173/QOI",
|
||||
"440x240/QOI",
|
||||
"480x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -66,8 +66,10 @@
|
|||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16",
|
||||
"313x173",
|
||||
"440x240"
|
||||
"16x16/QOI",
|
||||
"313x173/QOI",
|
||||
"440x240/QOI",
|
||||
"480x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -73,8 +73,10 @@
|
|||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16",
|
||||
"313x173",
|
||||
"440x240"
|
||||
"16x16/QOI",
|
||||
"313x173/QOI",
|
||||
"440x240/QOI",
|
||||
"480x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -73,8 +73,10 @@
|
|||
"nozzle_type": "hardened_steel",
|
||||
"auxiliary_fan": "0",
|
||||
"thumbnails": [
|
||||
"16x16",
|
||||
"313x173",
|
||||
"440x240"
|
||||
"16x16/QOI",
|
||||
"313x173/QOI",
|
||||
"440x240/QOI",
|
||||
"480x240/QOI",
|
||||
"640x480/PNG"
|
||||
]
|
||||
}
|
|
@ -19,10 +19,11 @@
|
|||
"0.4"
|
||||
],
|
||||
"thumbnails": [
|
||||
"380x380",
|
||||
"210x210"
|
||||
"380x380/COLPIC",
|
||||
"210x210/COLPIC",
|
||||
"380x380/PNG"
|
||||
],
|
||||
"thumbnails_format": "ColPic",
|
||||
"thumbnails_format": "PNG",
|
||||
"machine_start_gcode": "G28\nG0 Z50 F600\nM190 S[first_layer_bed_temperature]\nG28 Z\nG29 ; mesh bed leveling ,comment this code to close it\nG0 X0 Y0 Z50 F6000\nM109 S[first_layer_temperature]\nM83\nG0 Z5 F1200\nG0 X{first_layer_print_min[0]} Y{max(0, first_layer_print_min[1] - 2)} F12000\nG0 Z0.2 F600\nG1 E3 F1800\nG0 Z0.3 F600\nG1 X{min(first_layer_print_min[0] + 30,print_bed_max[0])} E6 F600",
|
||||
"machine_end_gcode": "M104 S0\nM140 S0\nG92 E0\nG1 E-3 F1800\nG90\n{if max_layer_z < max_print_height / 2}\nG1 Z{max_print_height / 2 + 10} F600\n{else}\nG1 Z{min(max_print_height, max_layer_z + 10)}\n{endif}\nG0 X5 Y{print_bed_max[1]-11} F12000\nM141 S0",
|
||||
"scan_first_layer": "0"
|
||||
|
|
|
@ -1014,6 +1014,10 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do legacy conversion on a completely loaded dictionary.
|
||||
// Perform composite conversions, for example merging multiple keys into one key.
|
||||
this->handle_legacy_composite();
|
||||
return 0;
|
||||
}
|
||||
catch (const std::ifstream::failure &err) {
|
||||
|
@ -1103,6 +1107,9 @@ ConfigSubstitutions ConfigBase::load(const boost::property_tree::ptree &tree, Fo
|
|||
// ignore
|
||||
}
|
||||
}
|
||||
// Do legacy conversion on a completely loaded dictionary.
|
||||
// Perform composite conversions, for example merging multiple keys into one key.
|
||||
this->handle_legacy_composite();
|
||||
return std::move(substitutions_ctxt.substitutions);
|
||||
}
|
||||
|
||||
|
@ -1189,7 +1196,10 @@ size_t ConfigBase::load_from_gcode_string_legacy(ConfigBase& config, const char*
|
|||
end = start;
|
||||
}
|
||||
|
||||
// BBS
|
||||
// Do legacy conversion on a completely loaded dictionary.
|
||||
// Perform composite conversions, for example merging multiple keys into one key.
|
||||
config.handle_legacy_composite();
|
||||
|
||||
free(result);
|
||||
return num_key_value_pairs;
|
||||
}
|
||||
|
@ -1368,6 +1378,10 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
|
|||
throw Slic3r::RuntimeError(format("Suspiciously low number of configuration values extracted from %1%: %2%", file, key_value_pairs));
|
||||
}
|
||||
|
||||
// Do legacy conversion on a completely loaded dictionary.
|
||||
// Perform composite conversions, for example merging multiple keys into one key.
|
||||
this->handle_legacy_composite();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": finished to parse_file %1%") % file.c_str();
|
||||
return std::move(substitutions_ctxt.substitutions);
|
||||
}
|
||||
|
|
|
@ -2057,6 +2057,10 @@ protected:
|
|||
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
|
||||
// handle_legacy() is called internally by set_deserialize().
|
||||
virtual void handle_legacy(t_config_option_key &/*opt_key*/, std::string &/*value*/) const {}
|
||||
// Called after a config is loaded as a whole.
|
||||
// Perform composite conversions, for example merging multiple keys into one key.
|
||||
// For conversion of single options, the handle_legacy() method above is called.
|
||||
virtual void handle_legacy_composite() {}
|
||||
|
||||
public:
|
||||
using ConfigOptionResolver::option;
|
||||
|
|
|
@ -1978,19 +1978,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
if (!print.config().small_area_infill_flow_compensation_model.empty())
|
||||
m_small_area_infill_flow_compensator = make_unique<SmallAreaInfillFlowCompensator>(print.config());
|
||||
|
||||
// if thumbnail type of BTT_TFT, insert above header
|
||||
// if not, it is inserted under the header in its normal spot
|
||||
GCodeThumbnailsFormat m_gcode_thumbnail_format = GCodeThumbnailsFormat::PNG;
|
||||
if (thumbnail_cb != nullptr) {
|
||||
m_gcode_thumbnail_format = print.full_print_config().opt_enum<GCodeThumbnailsFormat>("thumbnails_format");
|
||||
if (m_gcode_thumbnail_format == GCodeThumbnailsFormat::BTT_TFT)
|
||||
GCodeThumbnails::export_thumbnails_to_file(
|
||||
thumbnail_cb, print.get_plate_index(), print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values,
|
||||
m_gcode_thumbnail_format,
|
||||
[&file](const char *sz) { file.write(sz); },
|
||||
[&print]() { print.throw_if_canceled(); });
|
||||
}
|
||||
|
||||
file.write_format("; HEADER_BLOCK_START\n");
|
||||
// Write information on the generator.
|
||||
file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str());
|
||||
|
@ -2066,15 +2053,18 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
print.config().nozzle_temperature_initial_layer.get_at(0));
|
||||
file.write("; CONFIG_BLOCK_END\n\n");
|
||||
} else if (thumbnail_cb != nullptr) {
|
||||
if (m_gcode_thumbnail_format != GCodeThumbnailsFormat::BTT_TFT) {
|
||||
auto thumbnaim_fmt = m_gcode_thumbnail_format;
|
||||
// Orca: if the thumbnail format is ColPic, we write PNG in the beginning of gcode file and ColPic in the end of gcode file.
|
||||
if (m_gcode_thumbnail_format == GCodeThumbnailsFormat::ColPic)
|
||||
thumbnaim_fmt = GCodeThumbnailsFormat::PNG;
|
||||
GCodeThumbnails::export_thumbnails_to_file(
|
||||
thumbnail_cb, print.get_plate_index(), print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values,
|
||||
thumbnaim_fmt, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); });
|
||||
// generate the thumbnails
|
||||
auto [thumbnails, errors] = GCodeThumbnails::make_and_check_thumbnail_list(print.full_print_config());
|
||||
|
||||
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, print.get_plate_index(), thumbnails, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2781,11 +2771,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder)
|
||||
.c_str());
|
||||
file.write("\n");
|
||||
if (m_gcode_thumbnail_format == GCodeThumbnailsFormat::ColPic)
|
||||
GCodeThumbnails::export_thumbnails_to_file(
|
||||
thumbnail_cb, print.get_plate_index(), print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values,
|
||||
m_gcode_thumbnail_format, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); });
|
||||
|
||||
file.write("; CONFIG_BLOCK_START\n");
|
||||
std::string full_config;
|
||||
append_full_config(print, full_config);
|
||||
|
|
|
@ -528,5 +528,81 @@ int ColPic_EncodeStr(unsigned short* fromcolor16, int picw, int pich, unsigned c
|
|||
outputdata[qty] = 0;
|
||||
return qty;
|
||||
}
|
||||
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".
|
||||
// ??? If "thumbnails_format" is not defined, export to PNG.
|
||||
|
||||
// generate thumbnails data to process it
|
||||
|
||||
if (const auto thumbnails_value = config.option<ConfigOptionString>("thumbnails"))
|
||||
return make_and_check_thumbnail_list(thumbnails_value->value);
|
||||
|
||||
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%\"", "XxY/EXT, XxY/EXT, ...");
|
||||
if (errors.has(ThumbnailError::OutOfRange))
|
||||
error_str += "\n - Input value is out of range";
|
||||
if (errors.has(ThumbnailError::InvalidExt))
|
||||
error_str += "\n - Some extension in the input is invalid";
|
||||
|
||||
return error_str;
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GCodeThumbnails
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "../Point.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "ThumbnailData.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
@ -16,6 +15,12 @@
|
|||
|
||||
#include <boost/beast/core/detail/base64.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
enum class ThumbnailError : int { InvalidVal, OutOfRange, InvalidExt };
|
||||
using ThumbnailErrors = enum_bitmask<ThumbnailError>;
|
||||
ENABLE_ENUM_BITMASK_OPERATORS(ThumbnailError);
|
||||
}
|
||||
|
||||
namespace Slic3r::GCodeThumbnails {
|
||||
|
||||
struct CompressedImageBuffer
|
||||
|
@ -29,20 +34,30 @@ struct CompressedImageBuffer
|
|||
std::string get_hex(const unsigned int input);
|
||||
std::string rjust(std::string input, unsigned int width, char fill_char);
|
||||
std::unique_ptr<CompressedImageBuffer> compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format);
|
||||
std::string get_error_string(const ThumbnailErrors& errors);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
template<typename WriteToOutput, typename ThrowIfCanceledCallback>
|
||||
inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb,
|
||||
inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback& thumbnail_cb,
|
||||
int plate_id,
|
||||
const std::vector<Vec2d> &sizes,
|
||||
GCodeThumbnailsFormat format,
|
||||
const std::vector<std::pair<GCodeThumbnailsFormat, Vec2d>>& thumbnails_list,
|
||||
WriteToOutput output,
|
||||
ThrowIfCanceledCallback throw_if_canceled)
|
||||
{
|
||||
// Write thumbnails using base64 encoding
|
||||
if (thumbnail_cb != nullptr) {
|
||||
static constexpr const size_t max_row_length = 78;
|
||||
ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{sizes, true, true, true, true, plate_id});
|
||||
if (thumbnail_cb == nullptr)
|
||||
return;
|
||||
short i = 0;
|
||||
bool first_ColPic = true;
|
||||
for (const auto& [format, size] : thumbnails_list) {
|
||||
static constexpr const size_t max_row_length = 78;
|
||||
ThumbnailsList thumbnails = thumbnail_cb(ThumbnailsParams{{size}, true, true, true, true, plate_id});
|
||||
for (const ThumbnailData &data : thumbnails) {
|
||||
if (data.is_valid()) {
|
||||
auto compressed = compress_thumbnail(data, format);
|
||||
|
@ -51,15 +66,16 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb,
|
|||
// write BTT_TFT header
|
||||
output((";" + rjust(get_hex(data.width), 4, '0') + rjust(get_hex(data.height), 4, '0') + "\r\n").c_str());
|
||||
output((char *) compressed->data);
|
||||
if (i == (thumbnails.size() - 1))
|
||||
if (i == (thumbnails_list.size() - 1))
|
||||
output("; bigtree thumbnail end\r\n\r\n");
|
||||
}
|
||||
else if (format == GCodeThumbnailsFormat::ColPic) {
|
||||
if (i == 0) {
|
||||
if (first_ColPic) {
|
||||
output((boost::format("\n\n;gimage:%s\n\n") % reinterpret_cast<char*>(compressed->data)).str().c_str());
|
||||
} else {
|
||||
output((boost::format("\n\n;simage:%s\n\n") % reinterpret_cast<char*>(compressed->data)).str().c_str());
|
||||
}
|
||||
first_ColPic = false;
|
||||
}
|
||||
else {
|
||||
output("; THUMBNAIL_BLOCK_START\n");
|
||||
|
@ -84,11 +100,10 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb,
|
|||
}
|
||||
throw_if_canceled();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GCodeThumbnails
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
#include "Utils.hpp"
|
||||
#include "Time.hpp"
|
||||
#include "PlaceholderParser.hpp"
|
||||
#include "libslic3r/GCode/Thumbnails.hpp"
|
||||
|
||||
using boost::property_tree::ptree;
|
||||
|
||||
|
@ -2637,6 +2638,16 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
|
|||
} 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.
|
||||
// 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 contain extensions in old config but they are valid and use PNG extension by default
|
||||
// So, check if "thumbnails" is really changed
|
||||
// We will compare full thumbnails 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 {
|
||||
switch (other_opt->type()) {
|
||||
case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, config_other, config_this); break;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "Config.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include "GCode/Thumbnails.hpp"
|
||||
#include <set>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
@ -422,7 +423,7 @@ static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = {
|
|||
{ "JPG", int(GCodeThumbnailsFormat::JPG) },
|
||||
{ "QOI", int(GCodeThumbnailsFormat::QOI) },
|
||||
{ "BTT_TFT", int(GCodeThumbnailsFormat::BTT_TFT) },
|
||||
{ "ColPic", int(GCodeThumbnailsFormat::ColPic) }
|
||||
{ "COLPIC", int(GCodeThumbnailsFormat::ColPic) }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(GCodeThumbnailsFormat)
|
||||
|
||||
|
@ -4907,12 +4908,12 @@ def = this->add("filament_loading_speed", coFloats);
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("thumbnails", coPoints);
|
||||
def = this->add("thumbnails", coString);
|
||||
def->label = L("G-code thumbnails");
|
||||
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 / .sl1s files, in the following format: \"XxY, XxY, ...\"");
|
||||
def->mode = comAdvanced;
|
||||
def->gui_type = ConfigOptionDef::GUIType::one_string;
|
||||
def->set_default_value(new ConfigOptionPoints{Vec2d(300, 300)});
|
||||
def->set_default_value(new ConfigOptionString("48x48/PNG,300x300/PNG"));
|
||||
|
||||
def = this->add("thumbnails_format", coEnum);
|
||||
def->label = L("Format of G-code thumbnails");
|
||||
|
@ -4923,7 +4924,7 @@ def = this->add("filament_loading_speed", coFloats);
|
|||
def->enum_values.push_back("JPG");
|
||||
def->enum_values.push_back("QOI");
|
||||
def->enum_values.push_back("BTT_TFT");
|
||||
def->enum_values.push_back("ColPic");
|
||||
def->enum_values.push_back("COLPIC");
|
||||
def->enum_labels.push_back("PNG");
|
||||
def->enum_labels.push_back("JPG");
|
||||
def->enum_labels.push_back("QOI");
|
||||
|
@ -5950,6 +5951,60 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
}
|
||||
}
|
||||
|
||||
// Called after a config is loaded as a whole.
|
||||
// Perform composite conversions, for example merging multiple keys into one key.
|
||||
// Don't convert single options here, implement such conversion in PrintConfigDef::handle_legacy() instead.
|
||||
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")) {
|
||||
extention = opt->enum_values.at(config.option("thumbnails_format")->getInt());
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
if (config.has("wiping_volumes_matrix") && !config.has("wiping_volumes_use_custom_matrix")) {
|
||||
// This is apparently some pre-2.7.3 config, where the wiping_volumes_matrix was always used.
|
||||
// The 2.7.3 introduced an option to use defaults derived from config. In case the matrix
|
||||
// contains only default values, switch it to default behaviour. The default values
|
||||
// were zeros on the diagonal and 140 otherwise.
|
||||
std::vector<double> matrix = config.opt<ConfigOptionFloats>("wiping_volumes_matrix")->values;
|
||||
int num_of_extruders = int(std::sqrt(matrix.size()) + 0.5);
|
||||
int i = -1;
|
||||
bool custom = false;
|
||||
for (int j = 0; j < int(matrix.size()); ++j) {
|
||||
if (j % num_of_extruders == 0)
|
||||
++i;
|
||||
if (i != j % num_of_extruders && !is_approx(matrix[j], 140.)) {
|
||||
custom = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
config.set_key_value("wiping_volumes_use_custom_matrix", new ConfigOptionBool(custom));
|
||||
}
|
||||
}
|
||||
|
||||
const PrintConfigDef print_config_def;
|
||||
|
||||
DynamicPrintConfig DynamicPrintConfig::full_print_config()
|
||||
|
|
|
@ -419,6 +419,8 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType)
|
|||
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS
|
||||
|
||||
class DynamicPrintConfig;
|
||||
|
||||
// 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.
|
||||
class PrintConfigDef : public ConfigDef
|
||||
|
@ -427,6 +429,7 @@ public:
|
|||
PrintConfigDef();
|
||||
|
||||
static void handle_legacy(t_config_option_key &opt_key, std::string &value);
|
||||
static void handle_legacy_composite(DynamicPrintConfig &config);
|
||||
|
||||
// Array options growing with the number of extruders
|
||||
const std::vector<std::string>& extruder_option_keys() const { return m_extruder_option_keys; }
|
||||
|
@ -509,6 +512,12 @@ public:
|
|||
void handle_legacy(t_config_option_key &opt_key, std::string &value) const override
|
||||
{ PrintConfigDef::handle_legacy(opt_key, value); }
|
||||
|
||||
// Called after a config is loaded as a whole.
|
||||
// Perform composite conversions, for example merging multiple keys into one key.
|
||||
// For conversion of single options, the handle_legacy() method above is called.
|
||||
void handle_legacy_composite() override
|
||||
{ PrintConfigDef::handle_legacy_composite(*this); }
|
||||
|
||||
//BBS special case Support G/ Support W
|
||||
std::string get_filament_type(std::string &displayed_filament_type, int id = 0);
|
||||
|
||||
|
@ -1256,7 +1265,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||
((ConfigOptionFloat, nozzle_volume))
|
||||
((ConfigOptionPoints, start_end_points))
|
||||
((ConfigOptionEnum<TimelapseType>, timelapse_type))
|
||||
((ConfigOptionPoints, thumbnails))
|
||||
((ConfigOptionString, thumbnails))
|
||||
// BBS: move from PrintObjectConfig
|
||||
((ConfigOptionBool, independent_support_layer_height))
|
||||
// SoftFever
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Field.hpp"
|
||||
#include "libslic3r/GCode/Thumbnails.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
|
@ -83,6 +84,22 @@ wxString get_thumbnails_string(const std::vector<Vec2d>& values)
|
|||
return ret_str;
|
||||
}
|
||||
|
||||
ThumbnailErrors validate_thumbnails_string(wxString& str, const wxString& def_ext = "PNG")
|
||||
{
|
||||
std::string input_string = into_u8(str);
|
||||
|
||||
str.Clear();
|
||||
|
||||
auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(input_string);
|
||||
if (!thumbnails_list.empty()) {
|
||||
const auto& extentions = ConfigOptionEnum<GCodeThumbnailsFormat>::get_enum_names();
|
||||
for (const auto& [format, size] : thumbnails_list)
|
||||
str += format_wxstr("%1%x%2%/%3%, ", size.x(), size.y(), extentions[int(format)]);
|
||||
str.resize(str.Len() - 2);
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
Field::~Field()
|
||||
{
|
||||
|
@ -396,6 +413,31 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
set_value(stVal, false); // it's no needed but can be helpful, when inputted value contained "," instead of "."
|
||||
}
|
||||
}
|
||||
if (m_opt.opt_key == "thumbnails") {
|
||||
wxString str_out = str;
|
||||
ThumbnailErrors errors = validate_thumbnails_string(str_out);
|
||||
if (errors != enum_bitmask<ThumbnailError>()) {
|
||||
set_value(str_out, true);
|
||||
wxString error_str;
|
||||
if (errors.has(ThumbnailError::InvalidVal))
|
||||
error_str += format_wxstr(_L("Invalid input format. Expected vector of dimensions in the following format: \"%1%\""),
|
||||
"XxY/EXT, XxY/EXT, ...");
|
||||
if (errors.has(ThumbnailError::OutOfRange)) {
|
||||
if (!error_str.empty())
|
||||
error_str += "\n\n";
|
||||
error_str += _L("Input value is out of range");
|
||||
}
|
||||
if (errors.has(ThumbnailError::InvalidExt)) {
|
||||
if (!error_str.empty())
|
||||
error_str += "\n\n";
|
||||
error_str += _L("Some extension in the input is invalid");
|
||||
}
|
||||
show_error(m_parent, error_str);
|
||||
} else if (str_out != str) {
|
||||
str = str_out;
|
||||
set_value(str, true);
|
||||
}
|
||||
}
|
||||
|
||||
m_value = into_u8(str);
|
||||
break; }
|
||||
|
@ -434,16 +476,16 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
|
|||
if (!str.IsEmpty()) {
|
||||
bool invalid_val = false;
|
||||
bool out_of_range_val = false;
|
||||
wxStringTokenizer thumbnails(str, ",");
|
||||
while (thumbnails.HasMoreTokens()) {
|
||||
wxString token = thumbnails.GetNextToken();
|
||||
wxStringTokenizer points(str, ",");
|
||||
while (points.HasMoreTokens()) {
|
||||
wxString token = points.GetNextToken();
|
||||
double x, y;
|
||||
wxStringTokenizer thumbnail(token, "x");
|
||||
if (thumbnail.HasMoreTokens()) {
|
||||
wxString x_str = thumbnail.GetNextToken();
|
||||
if (x_str.ToDouble(&x) && thumbnail.HasMoreTokens()) {
|
||||
wxString y_str = thumbnail.GetNextToken();
|
||||
if (y_str.ToDouble(&y) && !thumbnail.HasMoreTokens()) {
|
||||
wxStringTokenizer _point(token, "x");
|
||||
if (_point.HasMoreTokens()) {
|
||||
wxString x_str = _point.GetNextToken();
|
||||
if (x_str.ToDouble(&x) && _point.HasMoreTokens()) {
|
||||
wxString y_str = _point.GetNextToken();
|
||||
if (y_str.ToDouble(&y) && !_point.HasMoreTokens()) {
|
||||
if (m_opt_id == "bed_exclude_area") {
|
||||
if (0 <= x && 0 <= y) {
|
||||
out_values.push_back(Vec2d(x, y));
|
||||
|
|
|
@ -692,8 +692,9 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config,
|
|||
opt_key == "printable_area" || opt_key == "compatible_printers" || opt_key == "compatible_prints" ||
|
||||
opt_key == "thumbnails" || opt_key == "bed_custom_texture" || opt_key == "bed_custom_model") {
|
||||
value = get_config_value(config, opt_key);
|
||||
this->change_opt_value(opt_key, value);
|
||||
OptionsGroup::on_change_OG(opt_key, value);
|
||||
set_value(opt_key, value);
|
||||
this->change_opt_value(opt_key, get_value(opt_key));
|
||||
OptionsGroup::on_change_OG(opt_key, get_value(opt_key));
|
||||
return;
|
||||
} else {
|
||||
auto opt_id = m_opt_map.find(opt_key)->first;
|
||||
|
@ -1072,8 +1073,6 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
|
|||
if (opt_key == "printable_area")
|
||||
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
else if (opt_key == "bed_exclude_area")
|
||||
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
else if (opt_key == "thumbnails")
|
||||
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
else
|
||||
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
|
||||
|
@ -1188,8 +1187,6 @@ boost::any ConfigOptionsGroup::get_config_value2(const DynamicPrintConfig& confi
|
|||
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
else if (opt_key == "bed_exclude_area")
|
||||
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
else if (opt_key == "thumbnails")
|
||||
ret = get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
else
|
||||
ret = config.option<ConfigOptionPoints>(opt_key)->get_at(idx);
|
||||
break;
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include "MarkdownTip.hpp"
|
||||
#include "Search.hpp"
|
||||
#include "BedShapeDialog.hpp"
|
||||
#include "libslic3r/GCode/Thumbnails.hpp"
|
||||
|
||||
#include "BedShapeDialog.hpp"
|
||||
// #include "BonjourDialog.hpp"
|
||||
|
@ -3669,7 +3670,43 @@ void TabPrinter::build_fff()
|
|||
option = optgroup->get_option("thumbnails");
|
||||
option.opt.full_width = true;
|
||||
optgroup->append_single_option_line(option, "thumbnails");
|
||||
optgroup->append_single_option_line("thumbnails_format", "thumbnails");
|
||||
// optgroup->append_single_option_line("thumbnails_format", "thumbnails");
|
||||
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any 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"
|
||||
const std::string val = boost::any_cast<std::string>(value);
|
||||
if (!value.empty()) {
|
||||
auto [thumbnails_list, errors] = GCodeThumbnails::make_and_check_thumbnail_list(val);
|
||||
|
||||
if (errors != enum_bitmask<ThumbnailError>()) {
|
||||
// TRN: First argument is parameter name, the second one is the value.
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_dirty();
|
||||
on_value_change(opt_key, value);
|
||||
});
|
||||
};
|
||||
|
||||
optgroup->append_single_option_line("use_relative_e_distances");
|
||||
optgroup->append_single_option_line("use_firmware_retraction");
|
||||
// optgroup->append_single_option_line("spaghetti_detector");
|
||||
|
|
|
@ -1367,9 +1367,6 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
|
|||
else if (opt_key == "bed_exclude_area") {
|
||||
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
}
|
||||
else if (opt_key == "thumbnails") {
|
||||
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
}
|
||||
else if (opt_key == "head_wrap_detect_zone") {
|
||||
return get_thumbnails_string(config.option<ConfigOptionPoints>(opt_key)->values);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue