Wip on svg archive import
This commit is contained in:
parent
e0fc337b2d
commit
4ef860811f
14 changed files with 592 additions and 347 deletions
|
@ -98,6 +98,8 @@ set(SLIC3R_SOURCES
|
|||
Format/SLAArchiveWriter.cpp
|
||||
Format/SLAArchiveReader.hpp
|
||||
Format/SLAArchiveReader.cpp
|
||||
Format/ZipperArchiveImport.hpp
|
||||
Format/ZipperArchiveImport.cpp
|
||||
Format/SL1.hpp
|
||||
Format/SL1.cpp
|
||||
Format/SL1_SVG.hpp
|
||||
|
|
|
@ -16,6 +16,20 @@
|
|||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
|
||||
#include "SLAArchiveReader.hpp"
|
||||
#include "ZipperArchiveImport.hpp"
|
||||
|
||||
#include "libslic3r/MarchingSquares.hpp"
|
||||
#include "libslic3r/PNGReadWrite.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/Execution/ExecutionTBB.hpp"
|
||||
|
||||
#include "libslic3r/SLA/RasterBase.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
using ConfMap = std::map<std::string, std::string>;
|
||||
|
@ -222,3 +236,238 @@ void SL1Archive::export_print(const std::string fname,
|
|||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
// Reader implementation
|
||||
// /////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace marchsq {
|
||||
|
||||
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
|
||||
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 {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template<class Fn> 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<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.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;
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> extract_slices_from_sla_archive(
|
||||
ZipperArchive &arch,
|
||||
const RasterParams &rstp,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
std::vector<ExPolygons> slices(arch.entries.size());
|
||||
|
||||
struct Status
|
||||
{
|
||||
double incr, val, prev;
|
||||
bool stop = false;
|
||||
execution::SpinningMutex<ExecutionTBB> mutex = {};
|
||||
} st{100. / slices.size(), 0., 0.};
|
||||
|
||||
execution::for_each(
|
||||
ex_tbb, size_t(0), arch.entries.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.entries[i].buf.data(),
|
||||
arch.entries[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 SL1Reader::read(std::vector<ExPolygons> &slices,
|
||||
DynamicPrintConfig &profile_out)
|
||||
{
|
||||
Vec2i windowsize;
|
||||
|
||||
switch(m_quality)
|
||||
{
|
||||
case SLAImportQuality::Fast: windowsize = {8, 8}; break;
|
||||
case SLAImportQuality::Balanced: windowsize = {4, 4}; break;
|
||||
default:
|
||||
case SLAImportQuality::Accurate:
|
||||
windowsize = {2, 2}; break;
|
||||
};
|
||||
|
||||
// Ensure minimum window size for marching squares
|
||||
windowsize.x() = std::max(2, windowsize.x());
|
||||
windowsize.y() = std::max(2, windowsize.y());
|
||||
|
||||
std::vector<std::string> includes = { "ini", "png"};
|
||||
std::vector<std::string> excludes = { "thumbnail" };
|
||||
ZipperArchive arch = read_zipper_archive(m_fname, includes, excludes);
|
||||
|
||||
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()};
|
||||
slices = extract_slices_from_sla_archive(arch, rstp, m_progr);
|
||||
|
||||
return config_substitutions;
|
||||
}
|
||||
|
||||
ConfigSubstitutions SL1Reader::read(DynamicPrintConfig &out)
|
||||
{
|
||||
ZipperArchive arch = read_zipper_archive(m_fname, {}, {"png"});
|
||||
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <string>
|
||||
|
||||
#include "SLAArchiveWriter.hpp"
|
||||
#include "SLAArchiveReader.hpp"
|
||||
|
||||
#include "libslic3r/Zipper.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
@ -37,6 +38,28 @@ public:
|
|||
const std::string &projectname = "") override;
|
||||
};
|
||||
|
||||
class SL1Reader: public SLAArchiveReader {
|
||||
SLAImportQuality m_quality = SLAImportQuality::Balanced;
|
||||
std::function<bool(int)> m_progr;
|
||||
std::string m_fname;
|
||||
|
||||
public:
|
||||
// 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 read(std::vector<ExPolygons> &slices,
|
||||
DynamicPrintConfig &profile_out) override;
|
||||
|
||||
ConfigSubstitutions read(DynamicPrintConfig &profile) override;
|
||||
|
||||
SL1Reader() = default;
|
||||
SL1Reader(const std::string &fname,
|
||||
SLAImportQuality quality,
|
||||
std::function<bool(int)> progr)
|
||||
: m_quality(quality), m_progr(progr), m_fname(fname)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
|
||||
#endif // ARCHIVETRAITS_HPP
|
||||
|
|
|
@ -3,6 +3,10 @@
|
|||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/Format/ZipperArchiveImport.hpp"
|
||||
|
||||
#define NANOSVG_IMPLEMENTATION
|
||||
#include "nanosvg/nanosvg.h"
|
||||
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
|
@ -234,4 +238,66 @@ void SL1_SVGArchive::export_print(const std::string fname,
|
|||
SL1Archive::export_print(zipper, print, thumbnails, projectname);
|
||||
}
|
||||
|
||||
ConfigSubstitutions SL1_SVGReader::read(std::vector<ExPolygons> &slices,
|
||||
DynamicPrintConfig &profile_out)
|
||||
{
|
||||
std::vector<std::string> includes = { "config.ini", "prusaslicer.ini", "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;
|
||||
|
||||
for (const EntryBuffer &entry : arch.entries) {
|
||||
NSVGimage* image;
|
||||
auto svgtxt = reserve_vector<char>(entry.buf.size());
|
||||
std::copy(entry.buf.begin(), entry.buf.end(), std::back_inserter(svgtxt));
|
||||
image = nsvgParse(svgtxt.data(), "px", 96);
|
||||
printf("size: %f x %f\n", image->width, image->height);
|
||||
// Use...
|
||||
for (NSVGshape *shape = image->shapes; shape != nullptr; shape = shape->next) {
|
||||
for (NSVGpath *path = shape->paths; path != nullptr; path = path->next) {
|
||||
|
||||
}
|
||||
}
|
||||
// Delete
|
||||
nsvgDelete(image);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
ConfigSubstitutions SL1_SVGReader::read(DynamicPrintConfig &out)
|
||||
{
|
||||
ZipperArchive arch = read_zipper_archive(m_fname, {"prusaslicer.ini"}, {});
|
||||
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -22,6 +22,28 @@ public:
|
|||
using SL1Archive::SL1Archive;
|
||||
};
|
||||
|
||||
class SL1_SVGReader: public SLAArchiveReader {
|
||||
SLAImportQuality m_quality = SLAImportQuality::Balanced;
|
||||
std::function<bool(int)> m_progr;
|
||||
std::string m_fname;
|
||||
|
||||
public:
|
||||
// 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 read(std::vector<ExPolygons> &slices,
|
||||
DynamicPrintConfig &profile_out) override;
|
||||
|
||||
ConfigSubstitutions read(DynamicPrintConfig &profile) override;
|
||||
|
||||
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)
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // SL1_SVG_HPP
|
||||
|
|
|
@ -1,223 +1,57 @@
|
|||
#include "SLAArchiveReader.hpp"
|
||||
#include "SL1.hpp"
|
||||
#include "SL1_SVG.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 <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <miniz.h>
|
||||
|
||||
namespace marchsq {
|
||||
|
||||
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
|
||||
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
|
||||
#include <array>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct PNGBuffer { std::vector<uint8_t> buf; std::string fname; };
|
||||
struct ArchiveData {
|
||||
boost::property_tree::ptree profile, config;
|
||||
std::vector<PNGBuffer> 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::unique_ptr<SLAArchiveReader> SLAArchiveReader::create(
|
||||
const std::string &fname,
|
||||
SLAImportQuality quality,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
std::string buf(size_t(entry.m_uncomp_size), '\0');
|
||||
std::string ext = boost::filesystem::path(fname).extension().string();
|
||||
boost::algorithm::to_lower(ext);
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw Slic3r::FileIOError(zip.get_errorstr());
|
||||
std::unique_ptr<SLAArchiveReader> ret;
|
||||
|
||||
boost::property_tree::ptree tree;
|
||||
std::stringstream ss(buf);
|
||||
boost::property_tree::read_ini(ss, tree);
|
||||
return tree;
|
||||
}
|
||||
const char *SL1_ext[] = {
|
||||
SLAArchiveWriter::get_extension("SL1"),
|
||||
"sl1s",
|
||||
// ...
|
||||
};
|
||||
|
||||
PNGBuffer read_png(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive & zip,
|
||||
const std::string & name)
|
||||
{
|
||||
std::vector<uint8_t> buf(entry.m_uncomp_size);
|
||||
const char *SL2_ext[] = {
|
||||
SLAArchiveWriter::get_extension("SL2"),
|
||||
"sl1_svg",
|
||||
// ...
|
||||
};
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw Slic3r::FileIOError(zip.get_errorstr());
|
||||
if (!ext.empty()) {
|
||||
if (ext.front() == '.')
|
||||
ext.erase(ext.begin());
|
||||
|
||||
return {std::move(buf), (name.empty() ? entry.m_filename : name)};
|
||||
}
|
||||
auto extcmp = [&ext](const auto &e) { return e == ext; };
|
||||
|
||||
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<std::string>()(r1.fname, r2.fname);
|
||||
});
|
||||
|
||||
arch.images.insert(it, read_png(entry, zip, name));
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
template<class Fn> 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<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.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;
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct SliceParams { double layerh = 0., initial_layerh = 0.; };
|
||||
|
||||
SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
||||
static SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *opt_layerh = cfg.option<ConfigOptionFloat>("layer_height");
|
||||
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
||||
|
@ -228,153 +62,42 @@ SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
|||
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> extract_slices_from_sla_archive(
|
||||
ArchiveData & arch,
|
||||
const RasterParams & rstp,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
auto jobdir = arch.config.get<std::string>("jobDir");
|
||||
for (auto &c : jobdir) c = std::tolower(c);
|
||||
|
||||
std::vector<ExPolygons> slices(arch.images.size());
|
||||
|
||||
struct Status
|
||||
{
|
||||
double incr, val, prev;
|
||||
bool stop = false;
|
||||
execution::SpinningMutex<ExecutionTBB> 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;
|
||||
}
|
||||
|
||||
// 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<bool(int)> 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<ExPolygons> 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
|
||||
|
||||
//inline ConfigSubstitutions import_sla_archive(
|
||||
// const std::string & zipfname,
|
||||
// Vec2i windowsize,
|
||||
// indexed_triangle_set & out,
|
||||
// std::function<bool(int)> progr = [](int) { return true; })
|
||||
//{
|
||||
// DynamicPrintConfig profile;
|
||||
// return import_sla_archive(zipfname, windowsize, out, profile, progr);
|
||||
//}
|
||||
|
||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
|
||||
{
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "png");
|
||||
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
}
|
||||
|
||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname,
|
||||
indexed_triangle_set &out,
|
||||
DynamicPrintConfig &profile,
|
||||
SLAImportQuality quality,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
Vec2i window;
|
||||
ConfigSubstitutions ret;
|
||||
|
||||
switch(quality)
|
||||
{
|
||||
case SLAImportQuality::Fast: window = {8, 8}; break;
|
||||
case SLAImportQuality:: Balanced: window = {4, 4}; break;
|
||||
default:
|
||||
case SLAImportQuality::Accurate:
|
||||
window = {2, 2};
|
||||
};
|
||||
if (auto reader = SLAArchiveReader::create(zipfname, quality, progr)) {
|
||||
std::vector<ExPolygons> slices;
|
||||
ret = reader->read(slices, profile);
|
||||
|
||||
return import_sla_archive(zipfname, window, out, profile, progr);
|
||||
SliceParams slicp = get_slice_params(profile);
|
||||
|
||||
if (!slices.empty())
|
||||
out = slices_to_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
|
||||
|
||||
} else {
|
||||
throw ReaderUnimplementedError("Reader unimplemented");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname,
|
||||
DynamicPrintConfig &out)
|
||||
{
|
||||
ConfigSubstitutions ret;
|
||||
|
||||
if (auto reader = SLAArchiveReader::create(zipfname)) {
|
||||
ret = reader->read(out);
|
||||
} else {
|
||||
throw ReaderUnimplementedError("Reader unimplemented");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -2,15 +2,37 @@
|
|||
#define SLAARCHIVEREADER_HPP
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
|
||||
struct indexed_triangle_set;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
||||
|
||||
enum class SLAImportQuality { Accurate, Balanced, Fast };
|
||||
|
||||
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
|
||||
|
||||
class SLAArchiveReader {
|
||||
public:
|
||||
|
||||
virtual ~SLAArchiveReader() = default;
|
||||
|
||||
virtual ConfigSubstitutions read(std::vector<ExPolygons> &slices,
|
||||
DynamicPrintConfig &profile) = 0;
|
||||
|
||||
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; });
|
||||
};
|
||||
|
||||
class ReaderUnimplementedError : public RuntimeError { using RuntimeError::RuntimeError; };
|
||||
|
||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname,
|
||||
DynamicPrintConfig &out);
|
||||
|
||||
ConfigSubstitutions import_sla_archive(
|
||||
const std::string &zipfname,
|
||||
indexed_triangle_set &out,
|
||||
|
@ -18,8 +40,6 @@ ConfigSubstitutions import_sla_archive(
|
|||
SLAImportQuality quality = SLAImportQuality::Balanced,
|
||||
std::function<bool(int)> progr = [](int) { return true; });
|
||||
|
||||
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // SLAARCHIVEREADER_HPP
|
||||
|
|
|
@ -62,7 +62,7 @@ const std::vector<const char*>& SLAArchiveWriter::registered_archives()
|
|||
|
||||
const char *SLAArchiveWriter::get_extension(const char *archtype)
|
||||
{
|
||||
static const char* DEFAULT_EXT = "zip";
|
||||
constexpr const char* DEFAULT_EXT = "zip";
|
||||
|
||||
auto entry = REGISTERED_ARCHIVES.find(archtype);
|
||||
if (entry != REGISTERED_ARCHIVES.end())
|
||||
|
|
|
@ -44,14 +44,15 @@ public:
|
|||
execution::max_concurrency(ep));
|
||||
}
|
||||
|
||||
// Export the print into an archive using the provided filename.
|
||||
// Export the print into an archive using the provided filename.
|
||||
virtual void export_print(const std::string fname,
|
||||
const SLAPrint &print,
|
||||
const ThumbnailsList &thumbnails,
|
||||
const std::string &projectname = "") = 0;
|
||||
|
||||
// Factory method to create an archiver instance
|
||||
static std::unique_ptr<SLAArchiveWriter> create(const std::string &archtype, const SLAPrinterConfig&);
|
||||
static std::unique_ptr<SLAArchiveWriter> create(
|
||||
const std::string &archtype, const SLAPrinterConfig &);
|
||||
|
||||
// Get the names of currently known archiver implementations
|
||||
static const std::vector<const char *> & registered_archives();
|
||||
|
|
103
src/libslic3r/Format/ZipperArchiveImport.cpp
Normal file
103
src/libslic3r/Format/ZipperArchiveImport.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include "ZipperArchiveImport.hpp"
|
||||
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/Exception.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
EntryBuffer read_entry(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive &zip,
|
||||
const std::string &name)
|
||||
{
|
||||
std::vector<uint8_t> 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)};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ZipperArchive read_zipper_archive(const std::string &zipfname,
|
||||
const std::vector<std::string> &includes,
|
||||
const std::vector<std::string> &excludes)
|
||||
{
|
||||
ZipperArchive 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 (!std::any_of(includes.begin(), includes.end(),
|
||||
[&name](const std::string &incl) {
|
||||
return boost::algorithm::contains(name, incl);
|
||||
}))
|
||||
continue;
|
||||
|
||||
if (std::any_of(excludes.begin(), excludes.end(),
|
||||
[&name](const std::string &excl) {
|
||||
return boost::algorithm::contains(name, excl);
|
||||
}))
|
||||
continue;
|
||||
|
||||
if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip);
|
||||
if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip);
|
||||
|
||||
auto it = std::lower_bound(
|
||||
arch.entries.begin(), arch.entries.end(),
|
||||
EntryBuffer{{}, name},
|
||||
[](const EntryBuffer &r1, const EntryBuffer &r2) {
|
||||
return std::less<std::string>()(r1.fname, r2.fname);
|
||||
});
|
||||
|
||||
arch.entries.insert(it, read_entry(entry, zip, name));
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
30
src/libslic3r/Format/ZipperArchiveImport.hpp
Normal file
30
src/libslic3r/Format/ZipperArchiveImport.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef ZIPPERARCHIVEIMPORT_HPP
|
||||
#define ZIPPERARCHIVEIMPORT_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct EntryBuffer
|
||||
{
|
||||
std::vector<uint8_t> buf;
|
||||
std::string fname;
|
||||
};
|
||||
|
||||
struct ZipperArchive
|
||||
{
|
||||
boost::property_tree::ptree profile, config;
|
||||
std::vector<EntryBuffer> entries;
|
||||
};
|
||||
|
||||
ZipperArchive read_zipper_archive(const std::string &zipfname,
|
||||
const std::vector<std::string> &includes,
|
||||
const std::vector<std::string> &excludes);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // ZIPPERARCHIVEIMPORT_HPP
|
|
@ -6,6 +6,8 @@
|
|||
#include "GUI_Utils.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#ifdef __WXGTK2__
|
||||
// Broken alpha workaround
|
||||
|
@ -13,7 +15,7 @@
|
|||
#include <wx/rawbmp.h>
|
||||
#endif /* __WXGTK2__ */
|
||||
|
||||
#define NANOSVG_IMPLEMENTATION
|
||||
//#define NANOSVG_IMPLEMENTATION
|
||||
#include "nanosvg/nanosvg.h"
|
||||
#define NANOSVGRAST_IMPLEMENTATION
|
||||
#include "nanosvg/nanosvgrast.h"
|
||||
|
|
|
@ -34,7 +34,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",
|
||||
"SL1 / SL1S archive files (*.sl1, *.sl1s, *.zip)|*.sl1;*.SL1;*.sl1s;*.SL1S;*.zip;*.ZIP|SL2 archive files (*.sl2)|*.sl2",
|
||||
wxDefaultPosition, wxDefaultSize, wxFLP_DEFAULT_STYLE | wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||
|
||||
szfilepck->Add(new wxStaticText(this, wxID_ANY, _L("Import file") + ": "), 0, wxALIGN_CENTER);
|
||||
|
|
|
@ -56,6 +56,7 @@ void SLAImportJob::process(Ctl &ctl)
|
|||
if (p->path.empty()) return;
|
||||
|
||||
std::string path = p->path.ToUTF8().data();
|
||||
|
||||
try {
|
||||
switch (p->sel) {
|
||||
case Sel::modelAndProfile:
|
||||
|
@ -69,9 +70,12 @@ void SLAImportJob::process(Ctl &ctl)
|
|||
break;
|
||||
}
|
||||
} catch (MissingProfileError &) {
|
||||
p->err = _L("The SLA archive doesn't contain any presets. "
|
||||
"Please activate some SLA printer preset first before importing that SLA archive.").ToStdString();
|
||||
} catch (std::exception &ex) {
|
||||
p->err = _u8L("The SLA archive doesn't contain any presets. "
|
||||
"Please activate some SLA printer preset first before "
|
||||
"importing that SLA archive.");
|
||||
} catch (ReaderUnimplementedError &) {
|
||||
p->err = _u8L("Import is unavailable for this archive format.");
|
||||
}catch (std::exception &ex) {
|
||||
p->err = ex.what();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue