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)
{
auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x");
@ -355,9 +327,31 @@ RasterParams get_raster_params(const DynamicPrintConfig &cfg)
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(
ZipperArchive &arch,
const RasterParams &rstp,
const marchsq::Coord &win,
std::function<bool(int)> progr)
{
std::vector<ExPolygons> slices(arch.entries.size());
@ -371,7 +365,7 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
execution::for_each(
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
{
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;
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,
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> excludes = { "thumbnail" };
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);
RasterParams rstp = get_raster_params(profile_use);
marchsq::Coord win = {windowsize.y(), windowsize.x()};
slices = extract_slices_from_sla_archive(arch, rstp, win, m_progr);
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);
rstp.win = {windowsize.y(), windowsize.x()};
slices = extract_slices_from_sla_archive(arch, rstp, m_progr);
return config_substitutions;
return std::move(config_substitutions);
}
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,
const sla::RasterBase::Trafo &trafo,
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)
{
// 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())
return;
@ -207,10 +192,10 @@ std::unique_ptr<sla::RasterBase> SL1_SVGArchive::create_raster() const
auto h = cfg().display_height.getFloat();
float precision_nm = scaled<float>(cfg().sla_output_precision.getFloat());
size_t res_x = std::round(scaled(w) / precision_nm);
size_t res_y = std::round(scaled(h) / precision_nm);
auto res_x = size_t(std::round(scaled(w) / precision_nm));
auto res_y = size_t(std::round(scaled(h) / precision_nm));
std::array<bool, 2> mirror;
std::array<bool, 2> mirror;
mirror[X] = cfg().display_mirror_x.getBool();
mirror[Y] = cfg().display_mirror_y.getBool();
@ -249,43 +234,11 @@ void SL1_SVGArchive::export_print(const std::string fname,
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 {
NSVGimage *image;
static constexpr const char *Units = "mm"; // Denotes user coordinate system
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); }
};
@ -294,33 +247,7 @@ ConfigSubstitutions SL1_SVGReader::read(std::vector<ExPolygons> &slices,
{
std::vector<std::string> includes = { CONFIG_FNAME, PROFILE_FNAME, "svg"};
ZipperArchive arch = read_zipper_archive(m_fname, includes, {});
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;
auto [profile_use, config_substitutions] = extract_profile(arch, profile_out);
RasterParams rstp = get_raster_params(profile_use);
@ -361,7 +288,8 @@ ConfigSubstitutions SL1_SVGReader::read(std::vector<ExPolygons> &slices,
slices.emplace_back(expolys);
}
return config_substitutions;
// Compile error without the move
return std::move(config_substitutions);
}
ConfigSubstitutions SL1_SVGReader::read(DynamicPrintConfig &out)

View file

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

View file

@ -7,48 +7,101 @@
#include <boost/filesystem/path.hpp>
#include <boost/algorithm/string.hpp>
constexpr const char * L(const char * str) { return str; }
#include <array>
#include <map>
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(
const std::string &fname,
SLAImportQuality quality,
std::function<bool(int)> progr)
const ProgrFn & progr)
{
std::string ext = boost::filesystem::path(fname).extension().string();
boost::algorithm::to_lower(ext);
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.front() == '.')
ext.erase(ext.begin());
auto extcmp = [&ext](const auto &e) { return e == ext; };
if (std::any_of(std::begin(SL1_ext), std::end(SL1_ext), extcmp)) {
ret = std::make_unique<SL1Reader>(fname, quality, progr);
} else if (std::any_of(std::begin(SL2_ext), std::end(SL2_ext), extcmp)) {
ret = std::make_unique<SL1_SVGReader>(fname, quality, progr);
for (const auto &[format_id, entry] : REGISTERED_ARCHIVES) {
if (std::any_of(entry.extensions.begin(), entry.extensions.end(), extcmp))
ret = entry.factoryfn(fname, quality, progr);
}
}
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.; };
static SliceParams get_slice_params(const DynamicPrintConfig &cfg)
@ -66,7 +119,7 @@ ConfigSubstitutions import_sla_archive(const std::string &zipfname,
indexed_triangle_set &out,
DynamicPrintConfig &profile,
SLAImportQuality quality,
std::function<bool(int)> progr)
const ProgrFn & progr)
{
ConfigSubstitutions ret;

View file

@ -12,6 +12,8 @@ enum class SLAImportQuality { Accurate, Balanced, Fast };
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
using ProgrFn = std::function<bool(int)>;
class SLAArchiveReader {
public:
@ -23,9 +25,17 @@ public:
virtual ConfigSubstitutions read(DynamicPrintConfig &profile) = 0;
static std::unique_ptr<SLAArchiveReader> create(
const std::string &fname,
SLAImportQuality quality = SLAImportQuality::Balanced,
std::function<bool(int)> progr = [](int){ return false; });
const std::string &fname,
SLAImportQuality quality = SLAImportQuality::Balanced,
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; };
@ -38,7 +48,7 @@ ConfigSubstitutions import_sla_archive(
indexed_triangle_set &out,
DynamicPrintConfig &profile,
SLAImportQuality quality = SLAImportQuality::Balanced,
std::function<bool(int)> progr = [](int) { return true; });
const ProgrFn &progr = [](int) { return true; });
} // namespace Slic3r

View file

@ -2,6 +2,7 @@
#include "libslic3r/miniz_extension.hpp"
#include "libslic3r/Exception.hpp"
#include "libslic3r/PrintConfig.hpp"
#include <boost/property_tree/ini_parser.hpp>
#include <boost/filesystem/path.hpp>
@ -97,4 +98,36 @@ ZipperArchive read_zipper_archive(const std::string &zipfname,
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

View file

@ -7,6 +7,8 @@
#include <boost/property_tree/ptree.hpp>
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r {
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> &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
#endif // ZIPPERARCHIVEIMPORT_HPP

View file

@ -10,6 +10,8 @@
#include <wx/filepicker.h>
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/Format/SLAArchiveReader.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI.hpp"
@ -21,6 +23,52 @@
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 {
wxFilePickerCtrl *m_filepicker;
wxComboBox *m_import_dropdown, *m_quality_dropdown;
@ -34,7 +82,7 @@ public:
m_filepicker = new wxFilePickerCtrl(this, wxID_ANY,
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);
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](){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](){return m_plater != nullptr && m_plater->get_ui_job_worker().is_idle(); }, this);