From 5ecf29f303b124c39409aad26bcbd972da0f857d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 20 Apr 2022 11:04:11 +0200 Subject: [PATCH] Separate existing sla archive import code --- src/libslic3r/CMakeLists.txt | 6 +- src/libslic3r/Format/SL1.cpp | 344 +---------------- src/libslic3r/Format/SL1.hpp | 25 +- src/libslic3r/Format/SLAArchiveReader.cpp | 352 ++++++++++++++++++ src/libslic3r/Format/SLAArchiveReader.hpp | 33 ++ .../{SLAArchive.cpp => SLAArchiveWriter.cpp} | 12 +- .../{SLAArchive.hpp => SLAArchiveWriter.hpp} | 6 +- src/libslic3r/Format/pwmx.hpp | 4 +- src/libslic3r/SLAPrint.cpp | 2 +- src/libslic3r/SLAPrint.hpp | 4 +- src/slic3r/GUI/Jobs/SLAImportJob.cpp | 1 + tests/sla_print/sla_archive_export_tests.cpp | 6 +- 12 files changed, 413 insertions(+), 382 deletions(-) create mode 100644 src/libslic3r/Format/SLAArchiveReader.cpp create mode 100644 src/libslic3r/Format/SLAArchiveReader.hpp rename src/libslic3r/Format/{SLAArchive.cpp => SLAArchiveWriter.cpp} (78%) rename src/libslic3r/Format/{SLAArchive.hpp => SLAArchiveWriter.hpp} (91%) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index a9ce8dd1f..e0492cc12 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -94,8 +94,10 @@ set(SLIC3R_SOURCES Format/objparser.hpp Format/STL.cpp Format/STL.hpp - Format/SLAArchive.hpp - Format/SLAArchive.cpp + Format/SLAArchiveWriter.hpp + Format/SLAArchiveWriter.cpp + Format/SLAArchiveReader.hpp + Format/SLAArchiveReader.cpp Format/SL1.hpp Format/SL1.cpp Format/SL1_SVG.hpp diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp index 2f324accd..accca4b77 100644 --- a/src/libslic3r/Format/SL1.cpp +++ b/src/libslic3r/Format/SL1.cpp @@ -1,359 +1,23 @@ #include "SL1.hpp" -#include "GCode/ThumbnailData.hpp" -#include "libslic3r/Time.hpp" #include #include -#include "libslic3r/Zipper.hpp" -#include "libslic3r/SLAPrint.hpp" - #include +#include "libslic3r/Time.hpp" +#include "libslic3r/Zipper.hpp" +#include "libslic3r/SLAPrint.hpp" #include "libslic3r/Exception.hpp" -#include "libslic3r/SlicesToTriangleMesh.hpp" -#include "libslic3r/MarchingSquares.hpp" -#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/MTUtils.hpp" #include "libslic3r/PrintConfig.hpp" -#include "libslic3r/SLA/RasterBase.hpp" + #include "libslic3r/miniz_extension.hpp" -#include "libslic3r/PNGReadWrite.hpp" #include "libslic3r/LocalesUtils.hpp" #include "libslic3r/GCode/ThumbnailData.hpp" -#include -#include -#include - -#include - -namespace marchsq { - -template<> struct _RasterTraits { - using Rst = Slic3r::png::ImageGreyscale; - - // The type of pixel cell in the raster - using ValueType = uint8_t; - - // Value at a given position - static uint8_t get(const Rst &rst, size_t row, size_t col) - { - return rst.get(row, col); - } - - // Number of rows and cols of the raster - static size_t rows(const Rst &rst) { return rst.rows; } - static size_t cols(const Rst &rst) { return rst.cols; } -}; - -} // namespace marchsq - namespace Slic3r { -namespace { - -struct PNGBuffer { std::vector buf; std::string fname; }; -struct ArchiveData { - boost::property_tree::ptree profile, config; - std::vector images; -}; - -static const constexpr char *CONFIG_FNAME = "config.ini"; -static const constexpr char *PROFILE_FNAME = "prusaslicer.ini"; - -boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, - MZ_Archive & zip) -{ - std::string buf(size_t(entry.m_uncomp_size), '\0'); - - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, - buf.data(), buf.size(), 0)) - throw Slic3r::FileIOError(zip.get_errorstr()); - - boost::property_tree::ptree tree; - std::stringstream ss(buf); - boost::property_tree::read_ini(ss, tree); - return tree; -} - -PNGBuffer read_png(const mz_zip_archive_file_stat &entry, - MZ_Archive & zip, - const std::string & name) -{ - std::vector buf(entry.m_uncomp_size); - - if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, - buf.data(), buf.size(), 0)) - throw Slic3r::FileIOError(zip.get_errorstr()); - - return {std::move(buf), (name.empty() ? entry.m_filename : name)}; -} - -ArchiveData extract_sla_archive(const std::string &zipfname, - const std::string &exclude) -{ - ArchiveData arch; - - // Little RAII - struct Arch: public MZ_Archive { - Arch(const std::string &fname) { - if (!open_zip_reader(&arch, fname)) - throw Slic3r::FileIOError(get_errorstr()); - } - - ~Arch() { close_zip_reader(&arch); } - } zip (zipfname); - - mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch); - - for (mz_uint i = 0; i < num_entries; ++i) - { - mz_zip_archive_file_stat entry; - - if (mz_zip_reader_file_stat(&zip.arch, i, &entry)) - { - std::string name = entry.m_filename; - boost::algorithm::to_lower(name); - - if (boost::algorithm::contains(name, exclude)) continue; - - if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip); - if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip); - - if (boost::filesystem::path(name).extension().string() == ".png") { - auto it = std::lower_bound( - arch.images.begin(), arch.images.end(), PNGBuffer{{}, name}, - [](const PNGBuffer &r1, const PNGBuffer &r2) { - return std::less()(r1.fname, r2.fname); - }); - - arch.images.insert(it, read_png(entry, zip, name)); - } - } - } - - return arch; -} - -ExPolygons rings_to_expolygons(const std::vector &rings, - double px_w, double px_h) -{ - auto polys = reserve_vector(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); -} - -template void foreach_vertex(ExPolygon &poly, Fn &&fn) -{ - for (auto &p : poly.contour.points) fn(p); - for (auto &h : poly.holes) - for (auto &p : h.points) fn(p); -} - -void invert_raster_trafo(ExPolygons & expolys, - const sla::RasterBase::Trafo &trafo, - coord_t width, - coord_t height) -{ - if (trafo.flipXY) std::swap(height, width); - - for (auto &expoly : expolys) { - if (trafo.mirror_y) - foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); }); - - if (trafo.mirror_x) - foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); }); - - expoly.translate(-trafo.center_x, -trafo.center_y); - - if (trafo.flipXY) - foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); }); - - if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) { - expoly.contour.reverse(); - for (auto &h : expoly.holes) h.reverse(); - } - } -} - -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("display_pixels_x"); - auto *opt_disp_rows = cfg.option("display_pixels_y"); - auto *opt_disp_w = cfg.option("display_width"); - auto *opt_disp_h = cfg.option("display_height"); - auto *opt_mirror_x = cfg.option("display_mirror_x"); - auto *opt_mirror_y = cfg.option("display_mirror_y"); - auto *opt_orient = cfg.option>("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.px_w = opt_disp_w->value / (opt_disp_cols->value - 1); - rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1); - - 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 SliceParams { double layerh = 0., initial_layerh = 0.; }; - -SliceParams get_slice_params(const DynamicPrintConfig &cfg) -{ - auto *opt_layerh = cfg.option("layer_height"); - auto *opt_init_layerh = cfg.option("initial_layer_height"); - - if (!opt_layerh || !opt_init_layerh) - throw MissingProfileError("Invalid SL1 / SL1S file"); - - return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; -} - -std::vector extract_slices_from_sla_archive( - ArchiveData & arch, - const RasterParams & rstp, - std::function progr) -{ - auto jobdir = arch.config.get("jobDir"); - for (auto &c : jobdir) c = std::tolower(c); - - std::vector slices(arch.images.size()); - - struct Status - { - double incr, val, prev; - bool stop = false; - tbb::spin_mutex mutex = {}; - } st {100. / slices.size(), 0., 0.}; - - tbb::parallel_for(size_t(0), arch.images.size(), - [&arch, &slices, &st, &rstp, progr](size_t i) { - // Status indication guarded with the spinlock - { - std::lock_guard lck(st.mutex); - if (st.stop) return; - - st.val += st.incr; - double curr = std::round(st.val); - if (curr > st.prev) { - st.prev = curr; - st.stop = !progr(int(curr)); - } - } - - png::ImageGreyscale img; - png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()}; - if (!png::decode_png(rb, img)) return; - - uint8_t isoval = 128; - auto rings = marchsq::execute(img, isoval, rstp.win); - ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h); - - // Invert the raster transformations indicated in the profile metadata - invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height); - - slices[i] = std::move(expolys); - }); - - if (st.stop) slices = {}; - - return slices; -} - -} // namespace - -ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out) -{ - ArchiveData arch = extract_sla_archive(zipfname, "png"); - return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable); -} - -// If the profile is missing from the archive (older PS versions did not have -// it), profile_out's initial value will be used as fallback. profile_out will be empty on -// function return if the archive did not contain any profile. -ConfigSubstitutions import_sla_archive( - const std::string & zipfname, - Vec2i windowsize, - indexed_triangle_set & out, - DynamicPrintConfig & profile_out, - std::function progr) -{ - // Ensure minimum window size for marching squares - windowsize.x() = std::max(2, windowsize.x()); - windowsize.y() = std::max(2, windowsize.y()); - - std::string exclude_entries{"thumbnail"}; - ArchiveData arch = extract_sla_archive(zipfname, exclude_entries); - 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); - rstp.win = {windowsize.y(), windowsize.x()}; - - SliceParams slicp = get_slice_params(profile_use); - - std::vector slices = - extract_slices_from_sla_archive(arch, rstp, progr); - - if (!slices.empty()) - out = slices_to_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); - - return config_substitutions; -} - using ConfMap = std::map; namespace { diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp index 0a662cc1e..a56c640ec 100644 --- a/src/libslic3r/Format/SL1.hpp +++ b/src/libslic3r/Format/SL1.hpp @@ -3,16 +3,14 @@ #include -#include "SLAArchive.hpp" +#include "SLAArchiveWriter.hpp" #include "libslic3r/Zipper.hpp" #include "libslic3r/PrintConfig.hpp" -struct indexed_triangle_set; - namespace Slic3r { -class SL1Archive: public SLAArchive { +class SL1Archive: public SLAArchiveWriter { SLAPrinterConfig m_cfg; protected: @@ -39,26 +37,7 @@ public: const std::string &projectname = "") override; }; -ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out); -ConfigSubstitutions import_sla_archive( - const std::string & zipfname, - Vec2i windowsize, - indexed_triangle_set & out, - DynamicPrintConfig & profile, - std::function progr = [](int) { return true; }); - -inline ConfigSubstitutions import_sla_archive( - const std::string & zipfname, - Vec2i windowsize, - indexed_triangle_set & out, - std::function progr = [](int) { return true; }) -{ - DynamicPrintConfig profile; - return import_sla_archive(zipfname, windowsize, out, profile, progr); -} - -class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; }; } // namespace Slic3r::sla diff --git a/src/libslic3r/Format/SLAArchiveReader.cpp b/src/libslic3r/Format/SLAArchiveReader.cpp new file mode 100644 index 000000000..3c7334a12 --- /dev/null +++ b/src/libslic3r/Format/SLAArchiveReader.cpp @@ -0,0 +1,352 @@ +#include "SLAArchiveReader.hpp" + +#include "libslic3r/MarchingSquares.hpp" +#include "libslic3r/SlicesToTriangleMesh.hpp" +#include "libslic3r/PNGReadWrite.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/Execution/ExecutionTBB.hpp" +#include "libslic3r/miniz_extension.hpp" + +#include "libslic3r/SLA/RasterBase.hpp" + +#include +#include +#include + +#include + +namespace marchsq { + +template<> struct _RasterTraits { + using Rst = Slic3r::png::ImageGreyscale; + + // The type of pixel cell in the raster + using ValueType = uint8_t; + + // Value at a given position + static uint8_t get(const Rst &rst, size_t row, size_t col) + { + return rst.get(row, col); + } + + // Number of rows and cols of the raster + static size_t rows(const Rst &rst) { return rst.rows; } + static size_t cols(const Rst &rst) { return rst.cols; } +}; + +} // namespace marchsq + +namespace Slic3r { + +struct PNGBuffer { std::vector buf; std::string fname; }; +struct ArchiveData { + boost::property_tree::ptree profile, config; + std::vector images; +}; + +static const constexpr char *CONFIG_FNAME = "config.ini"; +static const constexpr char *PROFILE_FNAME = "prusaslicer.ini"; + +namespace { + +boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry, + MZ_Archive & zip) +{ + std::string buf(size_t(entry.m_uncomp_size), '\0'); + + if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + buf.data(), buf.size(), 0)) + throw Slic3r::FileIOError(zip.get_errorstr()); + + boost::property_tree::ptree tree; + std::stringstream ss(buf); + boost::property_tree::read_ini(ss, tree); + return tree; +} + +PNGBuffer read_png(const mz_zip_archive_file_stat &entry, + MZ_Archive & zip, + const std::string & name) +{ + std::vector buf(entry.m_uncomp_size); + + if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename, + buf.data(), buf.size(), 0)) + throw Slic3r::FileIOError(zip.get_errorstr()); + + return {std::move(buf), (name.empty() ? entry.m_filename : name)}; +} + +ArchiveData extract_sla_archive(const std::string &zipfname, + const std::string &exclude) +{ + ArchiveData arch; + + // Little RAII + struct Arch: public MZ_Archive { + Arch(const std::string &fname) { + if (!open_zip_reader(&arch, fname)) + throw Slic3r::FileIOError(get_errorstr()); + } + + ~Arch() { close_zip_reader(&arch); } + } zip (zipfname); + + mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch); + + for (mz_uint i = 0; i < num_entries; ++i) + { + mz_zip_archive_file_stat entry; + + if (mz_zip_reader_file_stat(&zip.arch, i, &entry)) + { + std::string name = entry.m_filename; + boost::algorithm::to_lower(name); + + if (boost::algorithm::contains(name, exclude)) continue; + + if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip); + if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip); + + std::string ext = boost::filesystem::path(name).extension().string(); + boost::algorithm::to_lower(ext); + + if (ext == ".png") { + auto it = std::lower_bound( + arch.images.begin(), arch.images.end(), PNGBuffer{{}, name}, + [](const PNGBuffer &r1, const PNGBuffer &r2) { + return std::less()(r1.fname, r2.fname); + }); + + arch.images.insert(it, read_png(entry, zip, name)); + } + } + } + + return arch; +} + +ExPolygons rings_to_expolygons(const std::vector &rings, + double px_w, double px_h) +{ + auto polys = reserve_vector(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); +} + +template void foreach_vertex(ExPolygon &poly, Fn &&fn) +{ + for (auto &p : poly.contour.points) fn(p); + for (auto &h : poly.holes) + for (auto &p : h.points) fn(p); +} + +void invert_raster_trafo(ExPolygons & expolys, + const sla::RasterBase::Trafo &trafo, + coord_t width, + coord_t height) +{ + if (trafo.flipXY) std::swap(height, width); + + for (auto &expoly : expolys) { + if (trafo.mirror_y) + foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); }); + + if (trafo.mirror_x) + foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); }); + + expoly.translate(-trafo.center_x, -trafo.center_y); + + if (trafo.flipXY) + foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); }); + + if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) { + expoly.contour.reverse(); + for (auto &h : expoly.holes) h.reverse(); + } + } +} + +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("display_pixels_x"); + auto *opt_disp_rows = cfg.option("display_pixels_y"); + auto *opt_disp_w = cfg.option("display_width"); + auto *opt_disp_h = cfg.option("display_height"); + auto *opt_mirror_x = cfg.option("display_mirror_x"); + auto *opt_mirror_y = cfg.option("display_mirror_y"); + auto *opt_orient = cfg.option>("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.px_w = opt_disp_w->value / (opt_disp_cols->value - 1); + rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1); + + 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 SliceParams { double layerh = 0., initial_layerh = 0.; }; + +SliceParams get_slice_params(const DynamicPrintConfig &cfg) +{ + auto *opt_layerh = cfg.option("layer_height"); + auto *opt_init_layerh = cfg.option("initial_layer_height"); + + if (!opt_layerh || !opt_init_layerh) + throw MissingProfileError("Invalid SL1 / SL1S file"); + + return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()}; +} + +std::vector extract_slices_from_sla_archive( + ArchiveData & arch, + const RasterParams & rstp, + std::function progr) +{ + auto jobdir = arch.config.get("jobDir"); + for (auto &c : jobdir) c = std::tolower(c); + + std::vector slices(arch.images.size()); + + struct Status + { + double incr, val, prev; + bool stop = false; + execution::SpinningMutex mutex = {}; + } st{100. / slices.size(), 0., 0.}; + + execution::for_each( + ex_tbb, size_t(0), arch.images.size(), + [&arch, &slices, &st, &rstp, progr](size_t i) { + // Status indication guarded with the spinlock + { + std::lock_guard lck(st.mutex); + if (st.stop) return; + + st.val += st.incr; + double curr = std::round(st.val); + if (curr > st.prev) { + st.prev = curr; + st.stop = !progr(int(curr)); + } + } + + png::ImageGreyscale img; + png::ReadBuf rb{arch.images[i].buf.data(), + arch.images[i].buf.size()}; + if (!png::decode_png(rb, img)) return; + + constexpr uint8_t isoval = 128; + auto rings = marchsq::execute(img, isoval, rstp.win); + ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, + rstp.px_h); + + // Invert the raster transformations indicated in the profile metadata + invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height); + + slices[i] = std::move(expolys); + }, + execution::max_concurrency(ex_tbb)); + + if (st.stop) slices = {}; + + return slices; +} + +} // namespace + +ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out) +{ + ArchiveData arch = extract_sla_archive(zipfname, "png"); + return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable); +} + +// If the profile is missing from the archive (older PS versions did not have +// it), profile_out's initial value will be used as fallback. profile_out will be empty on +// function return if the archive did not contain any profile. +ConfigSubstitutions import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + indexed_triangle_set & out, + DynamicPrintConfig & profile_out, + std::function progr) +{ + // Ensure minimum window size for marching squares + windowsize.x() = std::max(2, windowsize.x()); + windowsize.y() = std::max(2, windowsize.y()); + + std::string exclude_entries{"thumbnail"}; + ArchiveData arch = extract_sla_archive(zipfname, exclude_entries); + 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); + rstp.win = {windowsize.y(), windowsize.x()}; + + SliceParams slicp = get_slice_params(profile_use); + + std::vector slices = + extract_slices_from_sla_archive(arch, rstp, progr); + + if (!slices.empty()) + out = slices_to_mesh(slices, 0, slicp.layerh, slicp.initial_layerh); + + return config_substitutions; +} + + +} // namespace Slic3r diff --git a/src/libslic3r/Format/SLAArchiveReader.hpp b/src/libslic3r/Format/SLAArchiveReader.hpp new file mode 100644 index 000000000..20ba3f385 --- /dev/null +++ b/src/libslic3r/Format/SLAArchiveReader.hpp @@ -0,0 +1,33 @@ +#ifndef SLAARCHIVEREADER_HPP +#define SLAARCHIVEREADER_HPP + +#include "libslic3r/PrintConfig.hpp" + +struct indexed_triangle_set; + +namespace Slic3r { + +ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out); + +ConfigSubstitutions import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + indexed_triangle_set & out, + DynamicPrintConfig & profile, + std::function progr = [](int) { return true; }); + +inline ConfigSubstitutions import_sla_archive( + const std::string & zipfname, + Vec2i windowsize, + indexed_triangle_set & out, + std::function progr = [](int) { return true; }) +{ + DynamicPrintConfig profile; + return import_sla_archive(zipfname, windowsize, out, profile, progr); +} + +class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; }; + +} // namespace Slic3r + +#endif // SLAARCHIVEREADER_HPP diff --git a/src/libslic3r/Format/SLAArchive.cpp b/src/libslic3r/Format/SLAArchiveWriter.cpp similarity index 78% rename from src/libslic3r/Format/SLAArchive.cpp rename to src/libslic3r/Format/SLAArchiveWriter.cpp index 8e2bf2824..02df9b5b3 100644 --- a/src/libslic3r/Format/SLAArchive.cpp +++ b/src/libslic3r/Format/SLAArchiveWriter.cpp @@ -1,4 +1,4 @@ -#include "SLAArchive.hpp" +#include "SLAArchiveWriter.hpp" #include "SL1.hpp" #include "SL1_SVG.hpp" @@ -13,7 +13,7 @@ namespace Slic3r { -using ArchiveFactory = std::function(const SLAPrinterConfig&)>; +using ArchiveFactory = std::function(const SLAPrinterConfig&)>; struct ArchiveEntry { const char *ext; @@ -35,8 +35,8 @@ static const std::map REGISTERED_ARCHIVES { } }; -std::unique_ptr -SLAArchive::create(const std::string &archtype, const SLAPrinterConfig &cfg) +std::unique_ptr +SLAArchiveWriter::create(const std::string &archtype, const SLAPrinterConfig &cfg) { auto entry = REGISTERED_ARCHIVES.find(archtype); @@ -46,7 +46,7 @@ SLAArchive::create(const std::string &archtype, const SLAPrinterConfig &cfg) return nullptr; } -const std::vector& SLAArchive::registered_archives() +const std::vector& SLAArchiveWriter::registered_archives() { static std::vector archnames; @@ -60,7 +60,7 @@ const std::vector& SLAArchive::registered_archives() return archnames; } -const char *SLAArchive::get_extension(const char *archtype) +const char *SLAArchiveWriter::get_extension(const char *archtype) { static const char* DEFAULT_EXT = "zip"; diff --git a/src/libslic3r/Format/SLAArchive.hpp b/src/libslic3r/Format/SLAArchiveWriter.hpp similarity index 91% rename from src/libslic3r/Format/SLAArchive.hpp rename to src/libslic3r/Format/SLAArchiveWriter.hpp index 971d7ba7b..1e591ef70 100644 --- a/src/libslic3r/Format/SLAArchive.hpp +++ b/src/libslic3r/Format/SLAArchiveWriter.hpp @@ -12,7 +12,7 @@ namespace Slic3r { class SLAPrint; class SLAPrinterConfig; -class SLAArchive { +class SLAArchiveWriter { protected: std::vector m_layers; @@ -20,7 +20,7 @@ protected: virtual sla::RasterEncoder get_encoder() const = 0; public: - virtual ~SLAArchive() = default; + virtual ~SLAArchiveWriter() = default; // Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid); template @@ -51,7 +51,7 @@ public: const std::string &projectname = "") = 0; // Factory method to create an archiver instance - static std::unique_ptr create(const std::string &archtype, const SLAPrinterConfig&); + static std::unique_ptr create(const std::string &archtype, const SLAPrinterConfig&); // Get the names of currently known archiver implementations static const std::vector & registered_archives(); diff --git a/src/libslic3r/Format/pwmx.hpp b/src/libslic3r/Format/pwmx.hpp index 65fb19910..6d667fab7 100644 --- a/src/libslic3r/Format/pwmx.hpp +++ b/src/libslic3r/Format/pwmx.hpp @@ -3,13 +3,13 @@ #include -#include "SLAArchive.hpp" +#include "SLAArchiveWriter.hpp" #include "libslic3r/PrintConfig.hpp" namespace Slic3r { -class PwmxArchive: public SLAArchive { +class PwmxArchive: public SLAArchiveWriter { SLAPrinterConfig m_cfg; protected: diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 203393e9c..c4494c3c2 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -246,7 +246,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con m_default_object_config.apply_only(config, object_diff, true); if (!m_archiver || !printer_diff.empty()) - m_archiver = SLAArchive::create(m_printer_config.sla_archive_format.value.c_str(), m_printer_config); + m_archiver = SLAArchiveWriter::create(m_printer_config.sla_archive_format.value.c_str(), m_printer_config); struct ModelObjectStatus { enum Status { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 6d8dc058b..56309b30b 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -9,7 +9,7 @@ #include "Point.hpp" #include "MTUtils.hpp" #include "Zipper.hpp" -#include "Format/SLAArchive.hpp" +#include "Format/SLAArchiveWriter.hpp" #include "GCode/ThumbnailData.hpp" #include "libslic3r/Execution/ExecutionTBB.hpp" @@ -524,7 +524,7 @@ private: std::vector m_printer_input; // The archive object which collects the raster images after slicing - std::unique_ptr m_archiver; + std::unique_ptr m_archiver; // Estimated print time, material consumed. SLAPrintStatistics m_print_statistics; diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp index b7dc64d52..2b29764e6 100644 --- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp +++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp @@ -2,6 +2,7 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Format/SL1.hpp" +#include "libslic3r/Format/SLAArchiveReader.hpp" #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/Plater.hpp" diff --git a/tests/sla_print/sla_archive_export_tests.cpp b/tests/sla_print/sla_archive_export_tests.cpp index 9dbe5bc64..2c0d68c3c 100644 --- a/tests/sla_print/sla_archive_export_tests.cpp +++ b/tests/sla_print/sla_archive_export_tests.cpp @@ -2,7 +2,7 @@ #include #include "libslic3r/SLAPrint.hpp" -#include "libslic3r/Format/SLAArchive.hpp" +#include "libslic3r/Format/SLAArchiveWriter.hpp" #include @@ -11,7 +11,7 @@ using namespace Slic3r; TEST_CASE("Archive export test", "[sla_archives]") { constexpr const char *PNAME = "20mm_cube"; - for (auto &archname : SLAArchive::registered_archives()) { + for (auto &archname : SLAArchiveWriter::registered_archives()) { INFO(std::string("Testing archive type: ") + archname); SLAPrint print; SLAFullPrintConfig fullcfg; @@ -30,7 +30,7 @@ TEST_CASE("Archive export test", "[sla_archives]") { print.process(); ThumbnailsList thumbnails; - auto outputfname = std::string("output.") + SLAArchive::get_extension(archname); + auto outputfname = std::string("output.") + SLAArchiveWriter::get_extension(archname); print.export_print(outputfname, thumbnails, PNAME);