Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortamasmeszaros <meszaros.q@gmail.com>2020-08-28 10:23:22 +0300
committertamasmeszaros <meszaros.q@gmail.com>2020-08-28 10:23:22 +0300
commit143e3a6a3588fe467e3cb3940cf78df30af56f96 (patch)
treedb5ec4461b2a448b449b0de4f6fce1cdac7fe86c /src
parent17170b81b51dfd7a99d1f3ef7f7d9038bf3502c0 (diff)
parent79567a1958f334e9c43208aa336e668e7da1a311 (diff)
Merge branch 'tm_sl1_import_libpng'
Diffstat (limited to 'src')
-rw-r--r--src/libslic3r/CMakeLists.txt4
-rw-r--r--src/libslic3r/Format/SL1.cpp308
-rw-r--r--src/libslic3r/Format/SL1.hpp18
-rw-r--r--src/libslic3r/PNGRead.cpp100
-rw-r--r--src/libslic3r/PNGRead.hpp70
-rw-r--r--src/libslic3r/SLA/AGGRaster.hpp9
-rw-r--r--src/slic3r/CMakeLists.txt2
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.cpp19
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.hpp2
-rw-r--r--src/slic3r/GUI/Jobs/SLAImportJob.cpp9
-rw-r--r--src/slic3r/Utils/SLAImport.cpp314
-rw-r--r--src/slic3r/Utils/SLAImport.hpp35
12 files changed, 524 insertions, 366 deletions
diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt
index 290b8953c..aea324722 100644
--- a/src/libslic3r/CMakeLists.txt
+++ b/src/libslic3r/CMakeLists.txt
@@ -161,6 +161,8 @@ add_library(libslic3r STATIC
PrintConfig.hpp
PrintObject.cpp
PrintRegion.cpp
+ PNGRead.hpp
+ PNGRead.cpp
Semver.cpp
ShortestPath.cpp
ShortestPath.hpp
@@ -308,6 +310,8 @@ target_link_libraries(libslic3r
TBB::tbb
libslic3r_cgal
${CMAKE_DL_LIBS}
+ PNG::PNG
+ ZLIB::ZLIB
)
if (TARGET OpenVDB::openvdb)
diff --git a/src/libslic3r/Format/SL1.cpp b/src/libslic3r/Format/SL1.cpp
index 5c402ef5b..ff1af5d8b 100644
--- a/src/libslic3r/Format/SL1.cpp
+++ b/src/libslic3r/Format/SL1.cpp
@@ -8,8 +8,316 @@
#include "libslic3r/Zipper.hpp"
#include "libslic3r/SLAPrint.hpp"
+#include <sstream>
+
+#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/PNGRead.hpp"
+
+#include <boost/property_tree/ini_parser.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/algorithm/string.hpp>
+
+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 {
+
+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";
+
+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 std::runtime_error(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<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 std::runtime_error(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 std::runtime_error(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<std::string>()(r1.fname, r2.fname);
+ });
+
+ arch.images.insert(it, read_png(entry, zip, name));
+ }
+ }
+ }
+
+ return arch;
+}
+
+ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
+ double px_w, double px_h)
+{
+ ExPolygons polys; polys.reserve(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);
+ }
+
+ // reverse the raster transformations
+ 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 std::runtime_error("Invalid SL1 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<ConfigOptionFloat>("layer_height");
+ auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
+
+ if (!opt_layerh || !opt_init_layerh)
+ throw std::runtime_error("Invalid SL1 file");
+
+ 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;
+ 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<tbb::spin_mutex> 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;
+
+ auto rings = marchsq::execute(img, 128, 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
+
+void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
+{
+ ArchiveData arch = extract_sla_archive(zipfname, "png");
+ out.load(arch.profile);
+}
+
+void import_sla_archive(
+ const std::string & zipfname,
+ Vec2i windowsize,
+ TriangleMesh & out,
+ DynamicPrintConfig & profile,
+ 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());
+
+ ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
+ profile.load(arch.profile);
+
+ RasterParams rstp = get_raster_params(profile);
+ rstp.win = {windowsize.y(), windowsize.x()};
+
+ SliceParams slicp = get_slice_params(profile);
+
+ std::vector<ExPolygons> slices =
+ extract_slices_from_sla_archive(arch, rstp, progr);
+
+ if (!slices.empty())
+ out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
+}
+
using ConfMap = std::map<std::string, std::string>;
namespace {
diff --git a/src/libslic3r/Format/SL1.hpp b/src/libslic3r/Format/SL1.hpp
index fbb6d6160..ab731ff84 100644
--- a/src/libslic3r/Format/SL1.hpp
+++ b/src/libslic3r/Format/SL1.hpp
@@ -38,6 +38,24 @@ public:
}
};
+void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
+
+void import_sla_archive(
+ const std::string & zipfname,
+ Vec2i windowsize,
+ TriangleMesh & out,
+ DynamicPrintConfig & profile,
+ std::function<bool(int)> progr = [](int) { return true; });
+
+inline void import_sla_archive(
+ const std::string & zipfname,
+ Vec2i windowsize,
+ TriangleMesh & out,
+ std::function<bool(int)> progr = [](int) { return true; })
+{
+ DynamicPrintConfig profile;
+ import_sla_archive(zipfname, windowsize, out, profile, progr);
+}
} // namespace Slic3r::sla
diff --git a/src/libslic3r/PNGRead.cpp b/src/libslic3r/PNGRead.cpp
new file mode 100644
index 000000000..e66143b84
--- /dev/null
+++ b/src/libslic3r/PNGRead.cpp
@@ -0,0 +1,100 @@
+#include "PNGRead.hpp"
+
+#include <memory>
+
+#include <cstdio>
+#include <png.h>
+
+namespace Slic3r { namespace png {
+
+struct PNGDescr {
+ png_struct *png = nullptr; png_info *info = nullptr;
+
+ PNGDescr() = default;
+ PNGDescr(const PNGDescr&) = delete;
+ PNGDescr(PNGDescr&&) = delete;
+ PNGDescr& operator=(const PNGDescr&) = delete;
+ PNGDescr& operator=(PNGDescr&&) = delete;
+
+ ~PNGDescr()
+ {
+ if (png && info) png_destroy_info_struct(png, &info);
+ if (png) png_destroy_read_struct( &png, nullptr, nullptr);
+ }
+};
+
+bool is_png(const ReadBuf &rb)
+{
+ static const constexpr int PNG_SIG_BYTES = 8;
+
+#if PNG_LIBPNG_VER_MINOR <= 2
+ // Earlier libpng versions had png_sig_cmp(png_bytep, ...) which is not
+ // a const pointer. It is not possible to cast away the const qualifier from
+ // the input buffer so... yes... life is challenging...
+ png_byte buf[PNG_SIG_BYTES];
+ auto inbuf = static_cast<const std::uint8_t *>(rb.buf);
+ std::copy(inbuf, inbuf + PNG_SIG_BYTES, buf);
+#else
+ auto buf = static_cast<png_const_bytep>(rb.buf);
+#endif
+
+ return rb.sz >= PNG_SIG_BYTES && !png_sig_cmp(buf, 0, PNG_SIG_BYTES);
+}
+
+// Buffer read callback for libpng. It provides an allocated output buffer and
+// the amount of data it desires to read from the input.
+void png_read_callback(png_struct *png_ptr,
+ png_bytep outBytes,
+ png_size_t byteCountToRead)
+{
+ // Retrieve our input buffer through the png_ptr
+ auto reader = static_cast<IStream *>(png_get_io_ptr(png_ptr));
+
+ if (!reader || !reader->is_ok()) return;
+
+ reader->read(static_cast<std::uint8_t *>(outBytes), byteCountToRead);
+}
+
+bool decode_png(IStream &in_buf, ImageGreyscale &out_img)
+{
+ static const constexpr int PNG_SIG_BYTES = 8;
+
+ std::vector<png_byte> sig(PNG_SIG_BYTES, 0);
+ in_buf.read(sig.data(), PNG_SIG_BYTES);
+ if (!png_check_sig(sig.data(), PNG_SIG_BYTES))
+ return false;
+
+ PNGDescr dsc;
+ dsc.png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr,
+ nullptr);
+
+ if(!dsc.png) return false;
+
+ dsc.info = png_create_info_struct(dsc.png);
+ if(!dsc.info) return false;
+
+ png_set_read_fn(dsc.png, static_cast<void *>(&in_buf), png_read_callback);
+
+ // Tell that we have already read the first bytes to check the signature
+ png_set_sig_bytes(dsc.png, PNG_SIG_BYTES);
+
+ png_read_info(dsc.png, dsc.info);
+
+ out_img.cols = png_get_image_width(dsc.png, dsc.info);
+ out_img.rows = png_get_image_height(dsc.png, dsc.info);
+ size_t color_type = png_get_color_type(dsc.png, dsc.info);
+ size_t bit_depth = png_get_bit_depth(dsc.png, dsc.info);
+
+ if (color_type != PNG_COLOR_TYPE_GRAY || bit_depth != 8)
+ return false;
+
+ out_img.buf.resize(out_img.rows * out_img.cols);
+
+ auto readbuf = static_cast<png_bytep>(out_img.buf.data());
+ for (size_t r = 0; r < out_img.rows; ++r)
+ png_read_row(dsc.png, readbuf + r * out_img.cols, nullptr);
+
+ return true;
+}
+
+}} // namespace Slic3r::png
diff --git a/src/libslic3r/PNGRead.hpp b/src/libslic3r/PNGRead.hpp
new file mode 100644
index 000000000..082edd569
--- /dev/null
+++ b/src/libslic3r/PNGRead.hpp
@@ -0,0 +1,70 @@
+#ifndef PNGREAD_HPP
+#define PNGREAD_HPP
+
+#include <vector>
+#include <string>
+#include <istream>
+
+namespace Slic3r { namespace png {
+
+// Interface for an input stream of encoded png image data.
+struct IStream {
+ virtual ~IStream() = default;
+ virtual size_t read(std::uint8_t *outp, size_t amount) = 0;
+ virtual bool is_ok() const = 0;
+};
+
+// The output format of decode_png: a 2D pixel matrix stored continuously row
+// after row (row major layout).
+template<class PxT> struct Image {
+ std::vector<PxT> buf;
+ size_t rows, cols;
+ PxT get(size_t row, size_t col) const { return buf[row * cols + col]; }
+};
+
+using ImageGreyscale = Image<uint8_t>;
+
+// Only decodes true 8 bit grayscale png images. Returns false for other formats
+// TODO (if needed): implement transformation of rgb images into grayscale...
+bool decode_png(IStream &stream, ImageGreyscale &out_img);
+
+// TODO (if needed)
+// struct RGB { uint8_t r, g, b; };
+// using ImageRGB = Image<RGB>;
+// bool decode_png(IStream &stream, ImageRGB &img);
+
+
+// Encoded png data buffer: a simple read-only buffer and its size.
+struct ReadBuf { const void *buf = nullptr; const size_t sz = 0; };
+
+bool is_png(const ReadBuf &pngbuf);
+
+template<class Img> bool decode_png(const ReadBuf &in_buf, Img &out_img)
+{
+ struct ReadBufStream: public IStream {
+ const ReadBuf &rbuf_ref; size_t pos = 0;
+
+ explicit ReadBufStream(const ReadBuf &buf): rbuf_ref{buf} {}
+
+ size_t read(std::uint8_t *outp, size_t amount) override
+ {
+ if (amount > rbuf_ref.sz - pos) return 0;
+
+ auto buf = static_cast<const std::uint8_t *>(rbuf_ref.buf);
+ std::copy(buf + pos, buf + (pos + amount), outp);
+ pos += amount;
+
+ return amount;
+ }
+
+ bool is_ok() const override { return pos < rbuf_ref.sz; }
+ } stream{in_buf};
+
+ return decode_png(stream, out_img);
+}
+
+// TODO: std::istream of FILE* could be similarly adapted in case its needed...
+
+}} // namespace Slic3r::png
+
+#endif // PNGREAD_HPP
diff --git a/src/libslic3r/SLA/AGGRaster.hpp b/src/libslic3r/SLA/AGGRaster.hpp
index 37baed9e8..917f718e9 100644
--- a/src/libslic3r/SLA/AGGRaster.hpp
+++ b/src/libslic3r/SLA/AGGRaster.hpp
@@ -128,12 +128,13 @@ protected:
}
public:
- template<class GammaFn> AGGRaster(const Resolution &res,
+ template<class GammaFn>
+ AGGRaster(const Resolution &res,
const PixelDim & pd,
const Trafo & trafo,
- const TColor & foreground,
- const TColor & background,
- GammaFn && gammafn)
+ const TColor & foreground,
+ const TColor & background,
+ GammaFn && gammafn)
: m_resolution(res)
, m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm)
, m_buf(res.pixels())
diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt
index 8a2672c77..b64cfdf4f 100644
--- a/src/slic3r/CMakeLists.txt
+++ b/src/slic3r/CMakeLists.txt
@@ -187,8 +187,6 @@ set(SLIC3R_GUI_SOURCES
Utils/HexFile.cpp
Utils/HexFile.hpp
Utils/Thread.hpp
- Utils/SLAImport.hpp
- Utils/SLAImport.cpp
)
if (APPLE)
diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp
index a326eea7b..44b2b6186 100644
--- a/src/slic3r/GUI/GUI_ObjectList.cpp
+++ b/src/slic3r/GUI/GUI_ObjectList.cpp
@@ -2209,7 +2209,7 @@ void ObjectList::load_shape_object(const std::string& type_name)
load_mesh_object(mesh, _(L("Shape")) + "-" + _(type_name));
}
-void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name)
+void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center)
{
// Add mesh to model as a new object
Model& model = wxGetApp().plater()->model();
@@ -2219,6 +2219,7 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
#endif /* _DEBUG */
std::vector<size_t> object_idxs;
+ auto bb = mesh.bounding_box();
ModelObject* new_object = model.add_object();
new_object->name = into_u8(name);
new_object->add_instance(); // each object should have at list one instance
@@ -2228,13 +2229,17 @@ void ObjectList::load_mesh_object(const TriangleMesh &mesh, const wxString &name
// set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
new_object->invalidate_bounding_box();
-
- new_object->center_around_origin();
+ new_object->translate(-bb.center());
+
+ if (center) {
+ const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
+ new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2)));
+ } else {
+ new_object->instances[0]->set_offset(bb.center());
+ }
+
new_object->ensure_on_bed();
-
- const BoundingBoxf bed_shape = wxGetApp().plater()->bed_shape_bb();
- new_object->instances[0]->set_offset(Slic3r::to_3d(bed_shape.center().cast<double>(), -new_object->origin_translation(2)));
-
+
object_idxs.push_back(model.objects.size() - 1);
#ifdef _DEBUG
check_model_ids_validity(model);
diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp
index aa5264b07..9f7dcd247 100644
--- a/src/slic3r/GUI/GUI_ObjectList.hpp
+++ b/src/slic3r/GUI/GUI_ObjectList.hpp
@@ -294,7 +294,7 @@ public:
void load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type);
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
void load_shape_object(const std::string &type_name);
- void load_mesh_object(const TriangleMesh &mesh, const wxString &name);
+ void load_mesh_object(const TriangleMesh &mesh, const wxString &name, bool center = true);
void del_object(const int obj_idx);
void del_subobject_item(wxDataViewItem& item);
void del_settings_from_config(const wxDataViewItem& parent_item);
diff --git a/src/slic3r/GUI/Jobs/SLAImportJob.cpp b/src/slic3r/GUI/Jobs/SLAImportJob.cpp
index 2d5d5b072..adecae6ac 100644
--- a/src/slic3r/GUI/Jobs/SLAImportJob.cpp
+++ b/src/slic3r/GUI/Jobs/SLAImportJob.cpp
@@ -1,10 +1,11 @@
#include "SLAImportJob.hpp"
+#include "libslic3r/Format/SL1.hpp"
+
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
-#include "slic3r/Utils/SLAImport.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/PresetBundle.hpp"
@@ -218,8 +219,10 @@ void SLAImportJob::finalize()
wxGetApp().load_current_presets();
}
- if (!p->mesh.empty())
- p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name);
+ if (!p->mesh.empty()) {
+ bool is_centered = false;
+ p->plater->sidebar().obj_list()->load_mesh_object(p->mesh, name, is_centered);
+ }
reset();
}
diff --git a/src/slic3r/Utils/SLAImport.cpp b/src/slic3r/Utils/SLAImport.cpp
deleted file mode 100644
index 442025a77..000000000
--- a/src/slic3r/Utils/SLAImport.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-#include "SLAImport.hpp"
-
-#include <sstream>
-
-#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 <boost/property_tree/ini_parser.hpp>
-#include <boost/filesystem/path.hpp>
-#include <boost/algorithm/string.hpp>
-
-#include <wx/image.h>
-#include <wx/mstream.h>
-
-namespace marchsq {
-
-// Specialize this struct to register a raster type for the Marching squares alg
-template<> struct _RasterTraits<wxImage> {
- using Rst = wxImage;
-
- // 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.GetRed(col, row);
- }
-
- // Number of rows and cols of the raster
- static size_t rows(const Rst &rst) { return rst.GetHeight(); }
- static size_t cols(const Rst &rst) { return rst.GetWidth(); }
-};
-
-} // namespace marchsq
-
-namespace Slic3r {
-
-namespace {
-
-struct ArchiveData {
- boost::property_tree::ptree profile, config;
- std::vector<sla::EncodedRaster> 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 std::runtime_error(zip.get_errorstr());
-
- boost::property_tree::ptree tree;
- std::stringstream ss(buf);
- boost::property_tree::read_ini(ss, tree);
- return tree;
-}
-
-sla::EncodedRaster 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);
-
- if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
- buf.data(), buf.size(), 0))
- throw std::runtime_error(zip.get_errorstr());
-
- return sla::EncodedRaster(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 std::runtime_error(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(), sla::EncodedRaster({}, name),
- [](const sla::EncodedRaster &r1, const sla::EncodedRaster &r2) {
- return std::less<std::string>()(r1.extension(), r2.extension());
- });
-
- arch.images.insert(it, read_png(entry, zip, name));
- }
- }
- }
-
- return arch;
-}
-
-ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
- double px_w, double px_h)
-{
- ExPolygons polys; polys.reserve(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);
- }
-
- // reverse the raster transformations
- 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)
-{
- 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 std::runtime_error("Invalid SL1 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);
-
- sla::RasterBase::Trafo 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<ConfigOptionFloat>("layer_height");
- auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
-
- if (!opt_layerh || !opt_init_layerh)
- throw std::runtime_error("Invalid SL1 file");
-
- 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;
- 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<tbb::spin_mutex> 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));
- }
- }
-
- auto &buf = arch.images[i];
- wxMemoryInputStream stream{buf.data(), buf.size()};
- wxImage img{stream};
-
- auto rings = marchsq::execute(img, 128, 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
-
-void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
-{
- ArchiveData arch = extract_sla_archive(zipfname, "png");
- out.load(arch.profile);
-}
-
-void import_sla_archive(
- const std::string & zipfname,
- Vec2i windowsize,
- TriangleMesh & out,
- DynamicPrintConfig & profile,
- 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());
-
- ArchiveData arch = extract_sla_archive(zipfname, "thumbnail");
- profile.load(arch.profile);
-
- RasterParams rstp = get_raster_params(profile);
- rstp.win = {windowsize.y(), windowsize.x()};
-
- SliceParams slicp = get_slice_params(profile);
-
- std::vector<ExPolygons> slices =
- extract_slices_from_sla_archive(arch, rstp, progr);
-
- if (!slices.empty())
- out = slices_to_triangle_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
-}
-
-} // namespace Slic3r
diff --git a/src/slic3r/Utils/SLAImport.hpp b/src/slic3r/Utils/SLAImport.hpp
deleted file mode 100644
index 73995014f..000000000
--- a/src/slic3r/Utils/SLAImport.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#ifndef SLAIMPORT_HPP
-#define SLAIMPORT_HPP
-
-#include <functional>
-
-#include <libslic3r/Point.hpp>
-#include <libslic3r/PrintConfig.hpp>
-
-namespace Slic3r {
-
-class TriangleMesh;
-class DynamicPrintConfig;
-
-void import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
-
-void import_sla_archive(
- const std::string & zipfname,
- Vec2i windowsize,
- TriangleMesh & out,
- DynamicPrintConfig & profile,
- std::function<bool(int)> progr = [](int) { return true; });
-
-inline void import_sla_archive(
- const std::string & zipfname,
- Vec2i windowsize,
- TriangleMesh & out,
- std::function<bool(int)> progr = [](int) { return true; })
-{
- DynamicPrintConfig profile;
- import_sla_archive(zipfname, windowsize, out, profile, progr);
-}
-
-}
-
-#endif // SLAIMPORT_HPP