Archive reader types are now registered in one place

This commit is contained in:
tamasmeszaros 2022-04-25 18:06:20 +02:00
parent 885e6964ba
commit 32a923da93
10 changed files with 225 additions and 167 deletions

View File

@ -297,34 +297,6 @@ void invert_raster_trafo(ExPolygons & expolys,
} }
} }
namespace {
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
double px_w, double px_h)
{
auto polys = reserve_vector<ExPolygon>(rings.size());
for (const marchsq::Ring &ring : rings) {
Polygon poly; Points &pts = poly.points;
pts.reserve(ring.size());
for (const marchsq::Coord &crd : ring)
pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h));
polys.emplace_back(poly);
}
// TODO: Is a union necessary?
return union_ex(polys);
}
struct RasterParams {
sla::RasterBase::Trafo trafo; // Raster transformations
coord_t width, height; // scaled raster dimensions (not resolution)
double px_h, px_w; // pixel dimesions
marchsq::Coord win; // marching squares window size
};
RasterParams get_raster_params(const DynamicPrintConfig &cfg) RasterParams get_raster_params(const DynamicPrintConfig &cfg)
{ {
auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x"); auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x");
@ -355,9 +327,31 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
return rstp; return rstp;
} }
namespace {
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
double px_w, double px_h)
{
auto polys = reserve_vector<ExPolygon>(rings.size());
for (const marchsq::Ring &ring : rings) {
Polygon poly; Points &pts = poly.points;
pts.reserve(ring.size());
for (const marchsq::Coord &crd : ring)
pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h));
polys.emplace_back(poly);
}
// TODO: Is a union necessary?
return union_ex(polys);
}
std::vector<ExPolygons> extract_slices_from_sla_archive( std::vector<ExPolygons> extract_slices_from_sla_archive(
ZipperArchive &arch, ZipperArchive &arch,
const RasterParams &rstp, const RasterParams &rstp,
const marchsq::Coord &win,
std::function<bool(int)> progr) std::function<bool(int)> progr)
{ {
std::vector<ExPolygons> slices(arch.entries.size()); std::vector<ExPolygons> slices(arch.entries.size());
@ -371,7 +365,7 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
execution::for_each( execution::for_each(
ex_tbb, size_t(0), arch.entries.size(), ex_tbb, size_t(0), arch.entries.size(),
[&arch, &slices, &st, &rstp, progr](size_t i) { [&arch, &slices, &st, &rstp, &win, progr](size_t i) {
// Status indication guarded with the spinlock // Status indication guarded with the spinlock
{ {
std::lock_guard lck(st.mutex); std::lock_guard lck(st.mutex);
@ -391,7 +385,7 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
if (!png::decode_png(rb, img)) return; if (!png::decode_png(rb, img)) return;
constexpr uint8_t isoval = 128; constexpr uint8_t isoval = 128;
auto rings = marchsq::execute(img, isoval, rstp.win); auto rings = marchsq::execute(img, isoval, win);
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w,
rstp.px_h); rstp.px_h);
@ -430,38 +424,13 @@ ConfigSubstitutions SL1Reader::read(std::vector<ExPolygons> &slices,
std::vector<std::string> includes = { "ini", "png"}; std::vector<std::string> includes = { "ini", "png"};
std::vector<std::string> excludes = { "thumbnail" }; std::vector<std::string> excludes = { "thumbnail" };
ZipperArchive arch = read_zipper_archive(m_fname, includes, excludes); ZipperArchive arch = read_zipper_archive(m_fname, includes, excludes);
auto [profile_use, config_substitutions] = extract_profile(arch, profile_out);
DynamicPrintConfig profile_in, profile_use;
ConfigSubstitutions config_substitutions =
profile_in.load(arch.profile,
ForwardCompatibilitySubstitutionRule::Enable);
if (profile_in.empty()) { // missing profile... do guess work
// try to recover the layer height from the config.ini which was
// present in all versions of sl1 files.
if (auto lh_opt = arch.config.find("layerHeight");
lh_opt != arch.config.not_found()) {
auto lh_str = lh_opt->second.data();
size_t pos;
double lh = string_to_double_decimal_point(lh_str, &pos);
if (pos) { // TODO: verify that pos is 0 when parsing fails
profile_out.set("layer_height", lh);
profile_out.set("initial_layer_height", lh);
}
}
}
// If the archive contains an empty profile, use the one that was passed as output argument
// then replace it with the readed profile to report that it was empty.
profile_use = profile_in.empty() ? profile_out : profile_in;
profile_out = profile_in;
RasterParams rstp = get_raster_params(profile_use); RasterParams rstp = get_raster_params(profile_use);
rstp.win = {windowsize.y(), windowsize.x()}; marchsq::Coord win = {windowsize.y(), windowsize.x()};
slices = extract_slices_from_sla_archive(arch, rstp, m_progr); slices = extract_slices_from_sla_archive(arch, rstp, win, m_progr);
return config_substitutions; return std::move(config_substitutions);
} }
ConfigSubstitutions SL1Reader::read(DynamicPrintConfig &out) ConfigSubstitutions SL1Reader::read(DynamicPrintConfig &out)

View File

@ -60,6 +60,14 @@ public:
{} {}
}; };
struct RasterParams {
sla::RasterBase::Trafo trafo; // Raster transformations
coord_t width, height; // scaled raster dimensions (not resolution)
double px_h, px_w; // pixel dimesions
};
RasterParams get_raster_params(const DynamicPrintConfig &cfg);
void invert_raster_trafo(ExPolygons & expolys, void invert_raster_trafo(ExPolygons & expolys,
const sla::RasterBase::Trafo &trafo, const sla::RasterBase::Trafo &trafo,
coord_t width, coord_t width,

View File

@ -77,21 +77,6 @@ void transform(ExPolygon &ep, const sla::RasterBase::Trafo &tr, const BoundingBo
void append_svg(std::string &buf, const Polygon &poly) void append_svg(std::string &buf, const Polygon &poly)
{ {
// if (poly.points.empty())
// return;
// char intbuf[coord_t_bufsize];
// buf += "<path d=\"M "sv;
// for (auto &p : poly) {
// buf += " "sv;
// buf += decimal_from(p.x(), intbuf);
// buf += " "sv;
// buf += decimal_from(p.y(), intbuf);
// }
// buf += " z\""sv; // mark path as closed
// buf += " />\n"sv;
if (poly.points.empty()) if (poly.points.empty())
return; return;
@ -207,8 +192,8 @@ std::unique_ptr<sla::RasterBase> SL1_SVGArchive::create_raster() const
auto h = cfg().display_height.getFloat(); auto h = cfg().display_height.getFloat();
float precision_nm = scaled<float>(cfg().sla_output_precision.getFloat()); float precision_nm = scaled<float>(cfg().sla_output_precision.getFloat());
size_t res_x = std::round(scaled(w) / precision_nm); auto res_x = size_t(std::round(scaled(w) / precision_nm));
size_t res_y = std::round(scaled(h) / precision_nm); auto res_y = size_t(std::round(scaled(h) / precision_nm));
std::array<bool, 2> mirror; std::array<bool, 2> mirror;
@ -249,43 +234,11 @@ void SL1_SVGArchive::export_print(const std::string fname,
SL1Archive::export_print(zipper, print, thumbnails, projectname); SL1Archive::export_print(zipper, print, thumbnails, projectname);
} }
struct RasterParams {
sla::RasterBase::Trafo trafo; // Raster transformations
coord_t width, height; // scaled raster dimensions (not resolution)
};
RasterParams get_raster_params(const DynamicPrintConfig &cfg)
{
auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x");
auto *opt_disp_rows = cfg.option<ConfigOptionInt>("display_pixels_y");
auto *opt_disp_w = cfg.option<ConfigOptionFloat>("display_width");
auto *opt_disp_h = cfg.option<ConfigOptionFloat>("display_height");
auto *opt_mirror_x = cfg.option<ConfigOptionBool>("display_mirror_x");
auto *opt_mirror_y = cfg.option<ConfigOptionBool>("display_mirror_y");
auto *opt_orient = cfg.option<ConfigOptionEnum<SLADisplayOrientation>>("display_orientation");
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
throw MissingProfileError("Invalid SL1 / SL1S file");
RasterParams rstp;
rstp.trafo = sla::RasterBase::Trafo{opt_orient->value == sladoLandscape ?
sla::RasterBase::roLandscape :
sla::RasterBase::roPortrait,
{opt_mirror_x->value, opt_mirror_y->value}};
rstp.height = scaled(opt_disp_h->value);
rstp.width = scaled(opt_disp_w->value);
return rstp;
}
struct NanoSVGParser { struct NanoSVGParser {
NSVGimage *image; NSVGimage *image;
static constexpr const char *Units = "mm"; // Denotes user coordinate system static constexpr const char *Units = "mm"; // Denotes user coordinate system
static constexpr float Dpi = 1.f; // Not needed static constexpr float Dpi = 1.f; // Not needed
NanoSVGParser(char* input): image{nsvgParse(input, Units, Dpi)} {} explicit NanoSVGParser(char* input): image{nsvgParse(input, Units, Dpi)} {}
~NanoSVGParser() { nsvgDelete(image); } ~NanoSVGParser() { nsvgDelete(image); }
}; };
@ -294,33 +247,7 @@ ConfigSubstitutions SL1_SVGReader::read(std::vector<ExPolygons> &slices,
{ {
std::vector<std::string> includes = { CONFIG_FNAME, PROFILE_FNAME, "svg"}; std::vector<std::string> includes = { CONFIG_FNAME, PROFILE_FNAME, "svg"};
ZipperArchive arch = read_zipper_archive(m_fname, includes, {}); ZipperArchive arch = read_zipper_archive(m_fname, includes, {});
auto [profile_use, config_substitutions] = extract_profile(arch, profile_out);
DynamicPrintConfig profile_in, profile_use;
ConfigSubstitutions config_substitutions =
profile_in.load(arch.profile,
ForwardCompatibilitySubstitutionRule::Enable);
if (profile_in.empty()) { // missing profile... do guess work
// try to recover the layer height from the config.ini which was
// present in all versions of sl1 files.
if (auto lh_opt = arch.config.find("layerHeight");
lh_opt != arch.config.not_found()) {
auto lh_str = lh_opt->second.data();
size_t pos;
double lh = string_to_double_decimal_point(lh_str, &pos);
if (pos) { // TODO: verify that pos is 0 when parsing fails
profile_out.set("layer_height", lh);
profile_out.set("initial_layer_height", lh);
}
}
}
// If the archive contains an empty profile, use the one that was passed as
// output argument then replace it with the readed profile to report that
// it was empty.
profile_use = profile_in.empty() ? profile_out : profile_in;
profile_out = profile_in;
RasterParams rstp = get_raster_params(profile_use); RasterParams rstp = get_raster_params(profile_use);
@ -361,7 +288,8 @@ ConfigSubstitutions SL1_SVGReader::read(std::vector<ExPolygons> &slices,
slices.emplace_back(expolys); slices.emplace_back(expolys);
} }
return config_substitutions; // Compile error without the move
return std::move(config_substitutions);
} }
ConfigSubstitutions SL1_SVGReader::read(DynamicPrintConfig &out) ConfigSubstitutions SL1_SVGReader::read(DynamicPrintConfig &out)

View File

@ -23,7 +23,6 @@ public:
}; };
class SL1_SVGReader: public SLAArchiveReader { class SL1_SVGReader: public SLAArchiveReader {
SLAImportQuality m_quality = SLAImportQuality::Balanced;
std::function<bool(int)> m_progr; std::function<bool(int)> m_progr;
std::string m_fname; std::string m_fname;
@ -38,9 +37,9 @@ public:
SL1_SVGReader() = default; SL1_SVGReader() = default;
SL1_SVGReader(const std::string &fname, SL1_SVGReader(const std::string &fname,
SLAImportQuality quality, SLAImportQuality /*quality*/,
std::function<bool(int)> progr) const ProgrFn & progr)
: m_quality(quality), m_progr(progr), m_fname(fname) : m_progr(progr), m_fname(fname)
{} {}
}; };

View File

@ -7,48 +7,101 @@
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
constexpr const char * L(const char * str) { return str; }
#include <array> #include <array>
#include <map>
namespace Slic3r { namespace Slic3r {
namespace {
using ArchiveFactory = std::function<
std::unique_ptr<SLAArchiveReader>(const std::string &fname,
SLAImportQuality quality,
const ProgrFn & progr)>;
struct ArchiveEntry {
const char *descr;
std::vector<const char *> extensions;
ArchiveFactory factoryfn;
};
static const std::map<std::string, ArchiveEntry> REGISTERED_ARCHIVES {
{
"SL1",
{ L("SL1 / SL1S archive files"), {"sl1", "sl1s", "zip"},
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique<SL1Reader>(fname, quality, progr); } }
},
{
"SL2",
{ L("SL2 archive files"), {"sl2", "sl1_svg", "zip"},
[] (const std::string &fname, SLAImportQuality quality, const ProgrFn &progr) { return std::make_unique<SL1_SVGReader>(fname, quality, progr); }}
},
// TODO: pwmx
};
} // namespace
std::unique_ptr<SLAArchiveReader> SLAArchiveReader::create( std::unique_ptr<SLAArchiveReader> SLAArchiveReader::create(
const std::string &fname, const std::string &fname,
SLAImportQuality quality, SLAImportQuality quality,
std::function<bool(int)> progr) const ProgrFn & progr)
{ {
std::string ext = boost::filesystem::path(fname).extension().string(); std::string ext = boost::filesystem::path(fname).extension().string();
boost::algorithm::to_lower(ext); boost::algorithm::to_lower(ext);
std::unique_ptr<SLAArchiveReader> ret; std::unique_ptr<SLAArchiveReader> ret;
const char *SL1_ext[] = {
SLAArchiveWriter::get_extension("SL1"),
"sl1s",
// ...
};
const char *SL2_ext[] = {
SLAArchiveWriter::get_extension("SL2"),
"sl1_svg",
// ...
};
if (!ext.empty()) { if (!ext.empty()) {
if (ext.front() == '.') if (ext.front() == '.')
ext.erase(ext.begin()); ext.erase(ext.begin());
auto extcmp = [&ext](const auto &e) { return e == ext; }; auto extcmp = [&ext](const auto &e) { return e == ext; };
if (std::any_of(std::begin(SL1_ext), std::end(SL1_ext), extcmp)) { for (const auto &[format_id, entry] : REGISTERED_ARCHIVES) {
ret = std::make_unique<SL1Reader>(fname, quality, progr); if (std::any_of(entry.extensions.begin(), entry.extensions.end(), extcmp))
} else if (std::any_of(std::begin(SL2_ext), std::end(SL2_ext), extcmp)) { ret = entry.factoryfn(fname, quality, progr);
ret = std::make_unique<SL1_SVGReader>(fname, quality, progr);
} }
} }
return ret; return ret;
} }
const std::vector<const char *> &SLAArchiveReader::registered_archives()
{
static std::vector<const char*> archnames;
if (archnames.empty()) {
archnames.reserve(REGISTERED_ARCHIVES.size());
for (auto &[name, _] : REGISTERED_ARCHIVES)
archnames.emplace_back(name.c_str());
}
return archnames;
}
std::vector<const char *> SLAArchiveReader::get_extensions(const char *archtype)
{
auto it = REGISTERED_ARCHIVES.find(archtype);
if (it != REGISTERED_ARCHIVES.end())
return it->second.extensions;
return {};
}
const char *SLAArchiveReader::get_description(const char *archtype)
{
auto it = REGISTERED_ARCHIVES.find(archtype);
if (it != REGISTERED_ARCHIVES.end())
return it->second.descr;
return nullptr;
}
struct SliceParams { double layerh = 0., initial_layerh = 0.; }; struct SliceParams { double layerh = 0., initial_layerh = 0.; };
static SliceParams get_slice_params(const DynamicPrintConfig &cfg) static SliceParams get_slice_params(const DynamicPrintConfig &cfg)
@ -66,7 +119,7 @@ ConfigSubstitutions import_sla_archive(const std::string &zipfname,
indexed_triangle_set &out, indexed_triangle_set &out,
DynamicPrintConfig &profile, DynamicPrintConfig &profile,
SLAImportQuality quality, SLAImportQuality quality,
std::function<bool(int)> progr) const ProgrFn & progr)
{ {
ConfigSubstitutions ret; ConfigSubstitutions ret;

View File

@ -12,6 +12,8 @@ enum class SLAImportQuality { Accurate, Balanced, Fast };
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; }; class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
using ProgrFn = std::function<bool(int)>;
class SLAArchiveReader { class SLAArchiveReader {
public: public:
@ -25,7 +27,15 @@ public:
static std::unique_ptr<SLAArchiveReader> create( static std::unique_ptr<SLAArchiveReader> create(
const std::string &fname, const std::string &fname,
SLAImportQuality quality = SLAImportQuality::Balanced, SLAImportQuality quality = SLAImportQuality::Balanced,
std::function<bool(int)> progr = [](int){ return false; }); const ProgrFn &progr = [](int) { return false; });
// Get the names of currently known archive reader implementations
static const std::vector<const char *> & registered_archives();
// Get the default file extensions belonging to an archive format
static std::vector<const char *> get_extensions(const char *archtype);
static const char * get_description(const char *archtype);
}; };
class ReaderUnimplementedError : public RuntimeError { using RuntimeError::RuntimeError; }; class ReaderUnimplementedError : public RuntimeError { using RuntimeError::RuntimeError; };
@ -38,7 +48,7 @@ ConfigSubstitutions import_sla_archive(
indexed_triangle_set &out, indexed_triangle_set &out,
DynamicPrintConfig &profile, DynamicPrintConfig &profile,
SLAImportQuality quality = SLAImportQuality::Balanced, SLAImportQuality quality = SLAImportQuality::Balanced,
std::function<bool(int)> progr = [](int) { return true; }); const ProgrFn &progr = [](int) { return true; });
} // namespace Slic3r } // namespace Slic3r

View File

@ -2,6 +2,7 @@
#include "libslic3r/miniz_extension.hpp" #include "libslic3r/miniz_extension.hpp"
#include "libslic3r/Exception.hpp" #include "libslic3r/Exception.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <boost/property_tree/ini_parser.hpp> #include <boost/property_tree/ini_parser.hpp>
#include <boost/filesystem/path.hpp> #include <boost/filesystem/path.hpp>
@ -97,4 +98,36 @@ ZipperArchive read_zipper_archive(const std::string &zipfname,
return arch; return arch;
} }
std::pair<DynamicPrintConfig, ConfigSubstitutions> extract_profile(
const ZipperArchive &arch, DynamicPrintConfig &profile_out)
{
DynamicPrintConfig profile_in, profile_use;
ConfigSubstitutions config_substitutions =
profile_in.load(arch.profile,
ForwardCompatibilitySubstitutionRule::Enable);
if (profile_in.empty()) { // missing profile... do guess work
// try to recover the layer height from the config.ini which was
// present in all versions of sl1 files.
if (auto lh_opt = arch.config.find("layerHeight");
lh_opt != arch.config.not_found()) {
auto lh_str = lh_opt->second.data();
size_t pos = 0;
double lh = string_to_double_decimal_point(lh_str, &pos);
if (pos) { // TODO: verify that pos is 0 when parsing fails
profile_out.set("layer_height", lh);
profile_out.set("initial_layer_height", lh);
}
}
}
// If the archive contains an empty profile, use the one that was passed as output argument
// then replace it with the readed profile to report that it was empty.
profile_use = profile_in.empty() ? profile_out : profile_in;
profile_out = profile_in;
return {profile_use, std::move(config_substitutions)};
}
} // namespace Slic3r } // namespace Slic3r

View File

@ -7,6 +7,8 @@
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r { namespace Slic3r {
struct EntryBuffer struct EntryBuffer
@ -28,6 +30,14 @@ ZipperArchive read_zipper_archive(const std::string &zipfname,
const std::vector<std::string> &includes, const std::vector<std::string> &includes,
const std::vector<std::string> &excludes); const std::vector<std::string> &excludes);
// Extract the print profile form the archive onto 'out'.
// Returns a profile that has correct parameters to use for model reconstruction
// even if the needed parameters were not fully found in the archive's metadata.
// The inout argument shall be a usable fallback profile if the archive
// has totally corrupted metadata.
std::pair<DynamicPrintConfig, ConfigSubstitutions> extract_profile(
const ZipperArchive &arch, DynamicPrintConfig &inout);
} // namespace Slic3r } // namespace Slic3r
#endif // ZIPPERARCHIVEIMPORT_HPP #endif // ZIPPERARCHIVEIMPORT_HPP

View File

@ -10,6 +10,8 @@
#include <wx/filepicker.h> #include <wx/filepicker.h>
#include "libslic3r/AppConfig.hpp" #include "libslic3r/AppConfig.hpp"
#include "libslic3r/Format/SLAArchiveReader.hpp"
#include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI.hpp"
@ -21,6 +23,52 @@
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
std::string get_readers_wildcard()
{
std::string ret;
for (const char *archtype : SLAArchiveReader::registered_archives()) {
ret += _utf8(SLAArchiveReader::get_description(archtype));
ret += " (";
auto extensions = SLAArchiveReader::get_extensions(archtype);
for (const char * ext : extensions) {
ret += "*.";
ret += ext;
ret += ", ";
}
// remove last ", "
if (!extensions.empty()) {
ret.pop_back();
ret.pop_back();
}
ret += ")|";
for (std::string ext : extensions) {
boost::algorithm::to_lower(ext);
ret += "*.";
ret += ext;
ret += ";";
boost::algorithm::to_upper(ext);
ret += "*.";
ret += ext;
ret += ";";
}
// remove last ';'
if (!extensions.empty())
ret.pop_back();
ret += "|";
}
if (ret.back() == '|')
ret.pop_back();
return ret;
}
class SLAImportDialog: public wxDialog, public SLAImportJobView { class SLAImportDialog: public wxDialog, public SLAImportJobView {
wxFilePickerCtrl *m_filepicker; wxFilePickerCtrl *m_filepicker;
wxComboBox *m_import_dropdown, *m_quality_dropdown; wxComboBox *m_import_dropdown, *m_quality_dropdown;
@ -34,7 +82,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 / SL1S archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP|SL2 archive files (*.sl2)|*.sl2", get_readers_wildcard(),
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);

View File

@ -1212,7 +1212,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 / SL1S Archive") + dots, _L("Load an SL1 / Sl1S archive"), append_menu_item(import_menu, wxID_ANY, _L("Import SLA Archive") + dots, _L("Load an SLA 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 && m_plater->get_ui_job_worker().is_idle(); }, this); [this](){return m_plater != nullptr && m_plater->get_ui_job_worker().is_idle(); }, this);