diff options
author | tamasmeszaros <meszaros.q@gmail.com> | 2019-06-20 11:03:47 +0300 |
---|---|---|
committer | tamasmeszaros <meszaros.q@gmail.com> | 2019-06-20 11:04:17 +0300 |
commit | c82fd692c3a67c439eec860a77155cee1b041aa8 (patch) | |
tree | 68d7507a7330a3c9acd01c084095693b175b472f /src/libslic3r/SLA | |
parent | d7684188f90c8eb2ae1f7bc881aa918d3177ecd6 (diff) | |
parent | 09f4831f4eba9fd5190d974904945f8c5c79b99f (diff) |
Merge branch 'master' into tm_builtin_pad
Diffstat (limited to 'src/libslic3r/SLA')
-rw-r--r-- | src/libslic3r/SLA/SLARaster.cpp | 329 | ||||
-rw-r--r-- | src/libslic3r/SLA/SLARaster.hpp | 150 | ||||
-rw-r--r-- | src/libslic3r/SLA/SLARasterWriter.cpp | 136 | ||||
-rw-r--r-- | src/libslic3r/SLA/SLARasterWriter.hpp | 167 | ||||
-rw-r--r-- | src/libslic3r/SLA/SLARotfinder.cpp | 4 | ||||
-rw-r--r-- | src/libslic3r/SLA/SLASupportTreeIGL.cpp | 17 | ||||
-rw-r--r-- | src/libslic3r/SLA/bicubic.h | 186 |
7 files changed, 974 insertions, 15 deletions
diff --git a/src/libslic3r/SLA/SLARaster.cpp b/src/libslic3r/SLA/SLARaster.cpp new file mode 100644 index 000000000..32a88b1b5 --- /dev/null +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -0,0 +1,329 @@ +#ifndef SLARASTER_CPP +#define SLARASTER_CPP + +#include <functional> + +#include "SLARaster.hpp" +#include "libslic3r/ExPolygon.hpp" +#include <libnest2d/backends/clipper/clipper_polygon.hpp> + +// For rasterizing +#include <agg/agg_basics.h> +#include <agg/agg_rendering_buffer.h> +#include <agg/agg_pixfmt_gray.h> +#include <agg/agg_pixfmt_rgb.h> +#include <agg/agg_renderer_base.h> +#include <agg/agg_renderer_scanline.h> + +#include <agg/agg_scanline_p.h> +#include <agg/agg_rasterizer_scanline_aa.h> +#include <agg/agg_path_storage.h> + +// Experimental minz image write: +#include <miniz.h> + +namespace Slic3r { + +inline const Polygon& contour(const ExPolygon& p) { return p.contour; } +inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; } + +inline const Polygons& holes(const ExPolygon& p) { return p.holes; } +inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; } + +namespace sla { + +class Raster::Impl { +public: + using TPixelRenderer = agg::pixfmt_gray8; // agg::pixfmt_rgb24; + using TRawRenderer = agg::renderer_base<TPixelRenderer>; + using TPixel = TPixelRenderer::color_type; + using TRawBuffer = agg::rendering_buffer; + + using TBuffer = std::vector<TPixelRenderer::pixel_type>; + + using TRendererAA = agg::renderer_scanline_aa_solid<TRawRenderer>; + + static const TPixel ColorWhite; + static const TPixel ColorBlack; + + using Format = Raster::Format; + +private: + Raster::Resolution m_resolution; +// Raster::PixelDim m_pxdim; + Raster::PixelDim m_pxdim_scaled; // used for scaled coordinate polygons + TBuffer m_buf; + TRawBuffer m_rbuf; + TPixelRenderer m_pixfmt; + TRawRenderer m_raw_renderer; + TRendererAA m_renderer; + + std::function<double(double)> m_gammafn; + std::array<bool, 2> m_mirror; + Format m_fmt = Format::PNG; + + inline void flipy(agg::path_storage& path) const { + path.flip_y(0, m_resolution.height_px); + } + + inline void flipx(agg::path_storage& path) const { + path.flip_x(0, m_resolution.width_px); + } + +public: + + inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd, + const std::array<bool, 2>& mirror, double gamma = 1.0): + m_resolution(res), +// m_pxdim(pd), + m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm), + m_buf(res.pixels()), + m_rbuf(reinterpret_cast<TPixelRenderer::value_type*>(m_buf.data()), + res.width_px, res.height_px, + int(res.width_px*TPixelRenderer::num_components)), + m_pixfmt(m_rbuf), + m_raw_renderer(m_pixfmt), + m_renderer(m_raw_renderer), + m_mirror(mirror) + { + m_renderer.color(ColorWhite); + + if(gamma > 0) m_gammafn = agg::gamma_power(gamma); + else m_gammafn = agg::gamma_threshold(0.5); + + clear(); + } + + inline Impl(const Raster::Resolution& res, + const Raster::PixelDim &pd, + Format fmt, + double gamma = 1.0): + Impl(res, pd, {false, false}, gamma) + { + switch (fmt) { + case Format::PNG: m_mirror = {false, true}; break; + case Format::RAW: m_mirror = {false, false}; break; + } + m_fmt = fmt; + } + + template<class P> void draw(const P &poly) { + agg::rasterizer_scanline_aa<> ras; + agg::scanline_p8 scanlines; + + ras.gamma(m_gammafn); + + auto&& path = to_path(contour(poly)); + + if(m_mirror[X]) flipx(path); + if(m_mirror[Y]) flipy(path); + + ras.add_path(path); + + for(auto& h : holes(poly)) { + auto&& holepath = to_path(h); + if(m_mirror[X]) flipx(holepath); + if(m_mirror[Y]) flipy(holepath); + ras.add_path(holepath); + } + + agg::render_scanlines(ras, scanlines, m_renderer); + } + + inline void clear() { + m_raw_renderer.clear(ColorBlack); + } + + inline TBuffer& buffer() { return m_buf; } + + inline Format format() const { return m_fmt; } + + inline const Raster::Resolution resolution() { return m_resolution; } + +private: + inline double getPx(const Point& p) { + return p(0) * m_pxdim_scaled.w_mm; + } + + inline double getPy(const Point& p) { + return p(1) * m_pxdim_scaled.h_mm; + } + + inline agg::path_storage to_path(const Polygon& poly) + { + return to_path(poly.points); + } + + inline double getPx(const ClipperLib::IntPoint& p) { + return p.X * m_pxdim_scaled.w_mm; + } + + inline double getPy(const ClipperLib::IntPoint& p) { + return p.Y * m_pxdim_scaled.h_mm; + } + + template<class PointVec> agg::path_storage to_path(const PointVec& poly) + { + agg::path_storage path; + + auto it = poly.begin(); + path.move_to(getPx(*it), getPy(*it)); + + while(++it != poly.end()) + path.line_to(getPx(*it), getPy(*it)); + + path.line_to(getPx(poly.front()), getPy(poly.front())); + return path; + } + +}; + +const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); +const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); + +template<> Raster::Raster() { reset(); }; +Raster::~Raster() = default; + +// Raster::Raster(Raster &&m) = default; +// Raster& Raster::operator=(Raster&&) = default; + +// FIXME: remove after migrating to higher version of windows compiler +Raster::Raster(Raster &&m): m_impl(std::move(m.m_impl)) {} +Raster& Raster::operator=(Raster &&m) { + m_impl = std::move(m.m_impl); return *this; +} + +void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, + Format fmt, double gamma) +{ + m_impl.reset(); + m_impl.reset(new Impl(r, pd, fmt, gamma)); +} + +void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, + const std::array<bool, 2>& mirror, double gamma) +{ + m_impl.reset(); + m_impl.reset(new Impl(r, pd, mirror, gamma)); +} + +void Raster::reset() +{ + m_impl.reset(); +} + +Raster::Resolution Raster::resolution() const +{ + if(m_impl) return m_impl->resolution(); + + return Resolution(0, 0); +} + +void Raster::clear() +{ + assert(m_impl); + m_impl->clear(); +} + +void Raster::draw(const ExPolygon &expoly) +{ + m_impl->draw(expoly); +} + +void Raster::draw(const ClipperLib::Polygon &poly) +{ + m_impl->draw(poly); +} + +void Raster::save(std::ostream& stream, Format fmt) +{ + assert(m_impl); + if(!stream.good()) return; + + switch(fmt) { + case Format::PNG: { + auto& b = m_impl->buffer(); + size_t out_len = 0; + void * rawdata = tdefl_write_image_to_png_file_in_memory( + b.data(), + int(resolution().width_px), + int(resolution().height_px), 1, &out_len); + + if(rawdata == nullptr) break; + + stream.write(static_cast<const char*>(rawdata), + std::streamsize(out_len)); + + MZ_FREE(rawdata); + + break; + } + case Format::RAW: { + stream << "P5 " + << m_impl->resolution().width_px << " " + << m_impl->resolution().height_px << " " + << "255 "; + + auto sz = m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type); + stream.write(reinterpret_cast<const char*>(m_impl->buffer().data()), + std::streamsize(sz)); + } + } +} + +void Raster::save(std::ostream &stream) +{ + save(stream, m_impl->format()); +} + +RawBytes Raster::save(Format fmt) +{ + assert(m_impl); + + std::vector<std::uint8_t> data; size_t s = 0; + + switch(fmt) { + case Format::PNG: { + void *rawdata = tdefl_write_image_to_png_file_in_memory( + m_impl->buffer().data(), + int(resolution().width_px), + int(resolution().height_px), 1, &s); + + if(rawdata == nullptr) break; + auto ptr = static_cast<std::uint8_t*>(rawdata); + + data.reserve(s); std::copy(ptr, ptr + s, std::back_inserter(data)); + + MZ_FREE(rawdata); + break; + } + case Format::RAW: { + auto header = std::string("P5 ") + + std::to_string(m_impl->resolution().width_px) + " " + + std::to_string(m_impl->resolution().height_px) + " " + "255 "; + + auto sz = m_impl->buffer().size()*sizeof(Impl::TBuffer::value_type); + s = sz + header.size(); + + data.reserve(s); + + auto buff = reinterpret_cast<std::uint8_t*>(m_impl->buffer().data()); + std::copy(header.begin(), header.end(), std::back_inserter(data)); + std::copy(buff, buff+sz, std::back_inserter(data)); + + break; + } + } + + return {std::move(data)}; +} + +RawBytes Raster::save() +{ + return save(m_impl->format()); +} + +} +} + +#endif // SLARASTER_CPP diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/SLARaster.hpp new file mode 100644 index 000000000..d3bd52d92 --- /dev/null +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -0,0 +1,150 @@ +#ifndef SLARASTER_HPP +#define SLARASTER_HPP + +#include <ostream> +#include <memory> +#include <vector> +#include <array> +#include <utility> +#include <cstdint> + +namespace ClipperLib { struct Polygon; } + +namespace Slic3r { + +class ExPolygon; + +namespace sla { + +// Raw byte buffer paired with its size. Suitable for compressed PNG data. +class RawBytes { + + std::vector<std::uint8_t> m_buffer; +public: + + RawBytes() = default; + RawBytes(std::vector<std::uint8_t>&& data): m_buffer(std::move(data)) {} + + size_t size() const { return m_buffer.size(); } + const uint8_t * data() { return m_buffer.data(); } + + RawBytes(const RawBytes&) = delete; + RawBytes& operator=(const RawBytes&) = delete; + + // ///////////////////////////////////////////////////////////////////////// + // FIXME: the following is needed for MSVC2013 compatibility + // ///////////////////////////////////////////////////////////////////////// + + // RawBytes(RawBytes&&) = default; + // RawBytes& operator=(RawBytes&&) = default; + + RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} + RawBytes& operator=(RawBytes&& mv) { + m_buffer = std::move(mv.m_buffer); + return *this; + } + + // ///////////////////////////////////////////////////////////////////////// +}; + +/** + * @brief Raster captures an anti-aliased monochrome canvas where vectorial + * polygons can be rasterized. Fill color is always white and the background is + * black. Contours are anti-aliased. + * + * It also supports saving the raster data into a standard output stream in raw + * or PNG format. + */ +class Raster { + class Impl; + std::unique_ptr<Impl> m_impl; +public: + + /// Supported compression types + enum class Format { + RAW, //!> Uncompressed pixel data + PNG //!> PNG compression + }; + + /// Type that represents a resolution in pixels. + struct Resolution { + unsigned width_px; + unsigned height_px; + + inline Resolution(unsigned w = 0, unsigned h = 0): + width_px(w), height_px(h) {} + + inline unsigned pixels() const /*noexcept*/ { + return width_px * height_px; + } + }; + + /// Types that represents the dimension of a pixel in millimeters. + struct PixelDim { + double w_mm; + double h_mm; + inline PixelDim(double px_width_mm = 0.0, double px_height_mm = 0.0): + w_mm(px_width_mm), h_mm(px_height_mm) {} + }; + + /// Constructor taking the resolution and the pixel dimension. + template <class...Args> Raster(Args...args) { + reset(std::forward<Args>(args)...); + } + + Raster(const Raster& cpy) = delete; + Raster& operator=(const Raster& cpy) = delete; + Raster(Raster&& m); + Raster& operator=(Raster&&); + ~Raster(); + + /// Reallocated everything for the given resolution and pixel dimension. + /// The third parameter is either the X, Y mirroring or a supported format + /// for which the correct mirroring will be configured. + void reset(const Resolution&, + const PixelDim&, + const std::array<bool, 2>& mirror, + double gamma = 1.0); + + void reset(const Resolution& r, + const PixelDim& pd, + Format o, + double gamma = 1.0); + + /** + * Release the allocated resources. Drawing in this state ends in + * unspecified behavior. + */ + void reset(); + + /// Get the resolution of the raster. + Resolution resolution() const; + + /// Clear the raster with black color. + void clear(); + + /// Draw a polygon with holes. + void draw(const ExPolygon& poly); + void draw(const ClipperLib::Polygon& poly); + + // Saving the raster: + // It is possible to override the format given in the constructor but + // be aware that the mirroring will not be modified. + + /// Save the raster on the specified stream. + void save(std::ostream& stream, Format); + void save(std::ostream& stream); + + /// Save into a continuous byte stream which is returned. + RawBytes save(Format fmt); + RawBytes save(); +}; + +// This prevents the duplicate default constructor warning on MSVC2013 +template<> Raster::Raster(); + + +} // sla +} // Slic3r + +#endif // SLARASTER_HPP diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp new file mode 100644 index 000000000..f7c3925ac --- /dev/null +++ b/src/libslic3r/SLA/SLARasterWriter.cpp @@ -0,0 +1,136 @@ +#include "SLARasterWriter.hpp" +#include "libslic3r/Zipper.hpp" +#include "ExPolygon.hpp" +#include <libnest2d/backends/clipper/clipper_polygon.hpp> + +#include <boost/log/trivial.hpp> +#include <boost/filesystem/path.hpp> + +namespace Slic3r { namespace sla { + +std::string SLARasterWriter::createIniContent(const std::string& projectname) const +{ + auto expt_str = std::to_string(m_exp_time_s); + auto expt_first_str = std::to_string(m_exp_time_first_s); + auto layerh_str = std::to_string(m_layer_height); + + const std::string cnt_fade_layers = std::to_string(m_cnt_fade_layers); + const std::string cnt_slow_layers = std::to_string(m_cnt_slow_layers); + const std::string cnt_fast_layers = std::to_string(m_cnt_fast_layers); + const std::string used_material = std::to_string(m_used_material); + + return std::string( + "action = print\n" + "jobDir = ") + projectname + "\n" + + "expTime = " + expt_str + "\n" + "expTimeFirst = " + expt_first_str + "\n" + "numFade = " + cnt_fade_layers + "\n" + "layerHeight = " + layerh_str + "\n" + "usedMaterial = " + used_material + "\n" + "numSlow = " + cnt_slow_layers + "\n" + "numFast = " + cnt_fast_layers + "\n"; +} + +void SLARasterWriter::flpXY(ClipperLib::Polygon &poly) +{ + for(auto& p : poly.Contour) std::swap(p.X, p.Y); + std::reverse(poly.Contour.begin(), poly.Contour.end()); + + for(auto& h : poly.Holes) { + for(auto& p : h) std::swap(p.X, p.Y); + std::reverse(h.begin(), h.end()); + } +} + +void SLARasterWriter::flpXY(ExPolygon &poly) +{ + for(auto& p : poly.contour.points) p = Point(p.y(), p.x()); + std::reverse(poly.contour.points.begin(), poly.contour.points.end()); + + for(auto& h : poly.holes) { + for(auto& p : h.points) p = Point(p.y(), p.x()); + std::reverse(h.points.begin(), h.points.end()); + } +} + +SLARasterWriter::SLARasterWriter(const SLAPrinterConfig &cfg, + const SLAMaterialConfig &mcfg, + double layer_height) +{ + double w = cfg.display_width.getFloat(); + double h = cfg.display_height.getFloat(); + auto pw = unsigned(cfg.display_pixels_x.getInt()); + auto ph = unsigned(cfg.display_pixels_y.getInt()); + + m_mirror[X] = cfg.display_mirror_x.getBool(); + + // PNG raster will implicitly do an Y mirror + m_mirror[Y] = ! cfg.display_mirror_y.getBool(); + + auto ro = cfg.display_orientation.getInt(); + + if(ro == roPortrait) { + std::swap(w, h); + std::swap(pw, ph); + m_o = roPortrait; + + // XY flipping implicitly does an X mirror + m_mirror[X] = ! m_mirror[X]; + } else m_o = roLandscape; + + m_res = Raster::Resolution(pw, ph); + m_pxdim = Raster::PixelDim(w/pw, h/ph); + m_exp_time_s = mcfg.exposure_time.getFloat(); + m_exp_time_first_s = mcfg.initial_exposure_time.getFloat(); + m_layer_height = layer_height; + + m_gamma = cfg.gamma_correction.getFloat(); +} + +void SLARasterWriter::save(const std::string &fpath, const std::string &prjname) +{ + try { + Zipper zipper(fpath); // zipper with no compression + + std::string project = prjname.empty()? + boost::filesystem::path(fpath).stem().string() : prjname; + + zipper.add_entry("config.ini"); + + zipper << createIniContent(project); + + for(unsigned i = 0; i < m_layers_rst.size(); i++) + { + if(m_layers_rst[i].rawbytes.size() > 0) { + char lyrnum[6]; + std::sprintf(lyrnum, "%.5d", i); + auto zfilename = project + lyrnum + ".png"; + + // Add binary entry to the zipper + zipper.add_entry(zfilename, + m_layers_rst[i].rawbytes.data(), + m_layers_rst[i].rawbytes.size()); + } + } + + zipper.finalize(); + } catch(std::exception& e) { + BOOST_LOG_TRIVIAL(error) << e.what(); + // Rethrow the exception + throw; + } +} + +void SLARasterWriter::set_statistics(const std::vector<double> statistics) +{ + if (statistics.size() != psCnt) + return; + + m_used_material = statistics[psUsedMaterial]; + m_cnt_fade_layers = int(statistics[psNumFade]); + m_cnt_slow_layers = int(statistics[psNumSlow]); + m_cnt_fast_layers = int(statistics[psNumFast]); +} + +} // namespace sla +} // namespace Slic3r diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/SLARasterWriter.hpp new file mode 100644 index 000000000..7133d2dde --- /dev/null +++ b/src/libslic3r/SLA/SLARasterWriter.hpp @@ -0,0 +1,167 @@ +#ifndef SLARASTERWRITER_HPP +#define SLARASTERWRITER_HPP + +// For png export of the sliced model +#include <fstream> +#include <sstream> +#include <vector> +#include <array> + +#include "libslic3r/PrintConfig.hpp" + +#include "SLARaster.hpp" + +namespace Slic3r { namespace sla { + +// Implementation for PNG raster output +// Be aware that if a large number of layers are allocated, it can very well +// exhaust the available memory especially on 32 bit platform. +// This class is designed to be used in parallel mode. Layers have an ID and +// each layer can be written and compressed independently (in parallel). +// At the end when all layers where written, the save method can be used to +// write out the result into a zipped archive. +class SLARasterWriter +{ +public: + enum RasterOrientation { + roLandscape, + roPortrait + }; + + // Used for addressing parameters of set_statistics() + enum ePrintStatistics + { + psUsedMaterial = 0, + psNumFade, + psNumSlow, + psNumFast, + + psCnt + }; + +private: + + // A struct to bind the raster image data and its compressed bytes together. + struct Layer { + Raster raster; + RawBytes rawbytes; + + Layer() = default; + Layer(const Layer&) = delete; // The image is big, do not copy by accident + Layer& operator=(const Layer&) = delete; + + // ///////////////////////////////////////////////////////////////////// + // FIXME: the following is needed for MSVC2013 compatibility + // ///////////////////////////////////////////////////////////////////// + + // Layer(Layer&& m) = default; + // Layer& operator=(Layer&&) = default; + Layer(Layer &&m): + raster(std::move(m.raster)), rawbytes(std::move(m.rawbytes)) {} + Layer& operator=(Layer &&m) { + raster = std::move(m.raster); rawbytes = std::move(m.rawbytes); + return *this; + } + }; + + // We will save the compressed PNG data into RawBytes type buffers in + // parallel. Later we can write every layer to the disk sequentially. + std::vector<Layer> m_layers_rst; + Raster::Resolution m_res; + Raster::PixelDim m_pxdim; + double m_exp_time_s = .0, m_exp_time_first_s = .0; + double m_layer_height = .0; + RasterOrientation m_o = roPortrait; + std::array<bool, 2> m_mirror; + + double m_gamma; + + double m_used_material = 0.0; + int m_cnt_fade_layers = 0; + int m_cnt_slow_layers = 0; + int m_cnt_fast_layers = 0; + + std::string createIniContent(const std::string& projectname) const; + + static void flpXY(ClipperLib::Polygon& poly); + static void flpXY(ExPolygon& poly); + +public: + + SLARasterWriter(const SLAPrinterConfig& cfg, + const SLAMaterialConfig& mcfg, + double layer_height); + + SLARasterWriter(const SLARasterWriter& ) = delete; + SLARasterWriter& operator=(const SLARasterWriter&) = delete; + + // ///////////////////////////////////////////////////////////////////////// + // FIXME: the following is needed for MSVC2013 compatibility + // ///////////////////////////////////////////////////////////////////////// + + // SLARasterWriter(SLARasterWriter&& m) = default; + // SLARasterWriter& operator=(SLARasterWriter&&) = default; + SLARasterWriter(SLARasterWriter&& m): + m_layers_rst(std::move(m.m_layers_rst)), + m_res(m.m_res), + m_pxdim(m.m_pxdim), + m_exp_time_s(m.m_exp_time_s), + m_exp_time_first_s(m.m_exp_time_first_s), + m_layer_height(m.m_layer_height), + m_o(m.m_o), + m_mirror(std::move(m.m_mirror)), + m_gamma(m.m_gamma), + m_used_material(m.m_used_material), + m_cnt_fade_layers(m.m_cnt_fade_layers), + m_cnt_slow_layers(m.m_cnt_slow_layers), + m_cnt_fast_layers(m.m_cnt_fast_layers) + {} + + // ///////////////////////////////////////////////////////////////////////// + + inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } + inline unsigned layers() const { return unsigned(m_layers_rst.size()); } + + template<class Poly> void draw_polygon(const Poly& p, unsigned lyr) { + assert(lyr < m_layers_rst.size()); + if(m_o == roPortrait) { + Poly poly(p); flpXY(poly); + m_layers_rst[lyr].raster.draw(poly); + } + else m_layers_rst[lyr].raster.draw(p); + } + + inline void begin_layer(unsigned lyr) { + if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1); + m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_mirror, m_gamma); + } + + inline void begin_layer() { + m_layers_rst.emplace_back(); + m_layers_rst.front().raster.reset(m_res, m_pxdim, m_mirror, m_gamma); + } + + inline void finish_layer(unsigned lyr_id) { + assert(lyr_id < m_layers_rst.size()); + m_layers_rst[lyr_id].rawbytes = + m_layers_rst[lyr_id].raster.save(Raster::Format::PNG); + m_layers_rst[lyr_id].raster.reset(); + } + + inline void finish_layer() { + if(!m_layers_rst.empty()) { + m_layers_rst.back().rawbytes = + m_layers_rst.back().raster.save(Raster::Format::PNG); + m_layers_rst.back().raster.reset(); + } + } + + void save(const std::string& fpath, const std::string& prjname = ""); + + void set_statistics(const std::vector<double> statistics); +}; + +} // namespace sla +} // namespace Slic3r + +#endif // SLARASTERWRITER_HPP diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/SLARotfinder.cpp index 1a91041b7..2e64059d6 100644 --- a/src/libslic3r/SLA/SLARotfinder.cpp +++ b/src/libslic3r/SLA/SLARotfinder.cpp @@ -44,7 +44,7 @@ std::array<double, 3> find_best_rotation(const ModelObject& modelobj, // call the status callback in each iteration but the actual value may be // the same for subsequent iterations (status goes from 0 to 100 but // iterations can be many more) - auto objfunc = [&emesh, &status, &statuscb, max_tries] + auto objfunc = [&emesh, &status, &statuscb, &stopcond, max_tries] (double rx, double ry, double rz) { EigenMesh3D& m = emesh; @@ -91,7 +91,7 @@ std::array<double, 3> find_best_rotation(const ModelObject& modelobj, } // report status - statuscb( unsigned(++status * 100.0/max_tries) ); + if(!stopcond()) statuscb( unsigned(++status * 100.0/max_tries) ); return score; }; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 04e6f79c7..4b6498a20 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -189,19 +189,10 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { V.resize(3*stl.stats.number_of_facets, 3); F.resize(stl.stats.number_of_facets, 3); for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) { - const stl_facet* facet = stl.facet_start+i; - V(3*i+0, 0) = double(facet->vertex[0](0)); - V(3*i+0, 1) = double(facet->vertex[0](1)); - V(3*i+0, 2) = double(facet->vertex[0](2)); - - V(3*i+1, 0) = double(facet->vertex[1](0)); - V(3*i+1, 1) = double(facet->vertex[1](1)); - V(3*i+1, 2) = double(facet->vertex[1](2)); - - V(3*i+2, 0) = double(facet->vertex[2](0)); - V(3*i+2, 1) = double(facet->vertex[2](1)); - V(3*i+2, 2) = double(facet->vertex[2](2)); - + const stl_facet &facet = stl.facet_start[i]; + V.block<1, 3>(3 * i + 0, 0) = facet.vertex[0].cast<double>(); + V.block<1, 3>(3 * i + 1, 0) = facet.vertex[1].cast<double>(); + V.block<1, 3>(3 * i + 2, 0) = facet.vertex[2].cast<double>(); F(i, 0) = int(3*i+0); F(i, 1) = int(3*i+1); F(i, 2) = int(3*i+2); diff --git a/src/libslic3r/SLA/bicubic.h b/src/libslic3r/SLA/bicubic.h new file mode 100644 index 000000000..870d00dbd --- /dev/null +++ b/src/libslic3r/SLA/bicubic.h @@ -0,0 +1,186 @@ +#ifndef BICUBIC_HPP +#define BICUBIC_HPP + +#include <algorithm> +#include <vector> +#include <cmath> + +#include <Eigen/Dense> + +namespace Slic3r { + +namespace BicubicInternal { + // Linear kernel, to be able to test cubic methods with hat kernels. + template<typename T> + struct LinearKernel + { + typedef T FloatType; + + static T a00() { return T(0.); } + static T a01() { return T(0.); } + static T a02() { return T(0.); } + static T a03() { return T(0.); } + static T a10() { return T(1.); } + static T a11() { return T(-1.); } + static T a12() { return T(0.); } + static T a13() { return T(0.); } + static T a20() { return T(0.); } + static T a21() { return T(1.); } + static T a22() { return T(0.); } + static T a23() { return T(0.); } + static T a30() { return T(0.); } + static T a31() { return T(0.); } + static T a32() { return T(0.); } + static T a33() { return T(0.); } + }; + + // Interpolation kernel aka Catmul-Rom aka Keyes kernel. + template<typename T> + struct CubicCatmulRomKernel + { + typedef T FloatType; + + static T a00() { return 0; } + static T a01() { return (T)-0.5; } + static T a02() { return (T) 1.; } + static T a03() { return (T)-0.5; } + static T a10() { return (T) 1.; } + static T a11() { return 0; } + static T a12() { return (T)-5./2.; } + static T a13() { return (T) 3./2.; } + static T a20() { return 0; } + static T a21() { return (T) 0.5; } + static T a22() { return (T) 2.; } + static T a23() { return (T)-3./2.; } + static T a30() { return 0; } + static T a31() { return 0; } + static T a32() { return (T)-0.5; } + static T a33() { return (T) 0.5; } + }; + + // B-spline kernel + template<typename T> + struct CubicBSplineKernel + { + typedef T FloatType; + + static T a00() { return (T) 1./6.; } + static T a01() { return (T) -3./6.; } + static T a02() { return (T) 3./6.; } + static T a03() { return (T) -1./6.; } + static T a10() { return (T) 4./6.; } + static T a11() { return 0; } + static T a12() { return (T) -6./6.; } + static T a13() { return (T) 3./6.; } + static T a20() { return (T) 1./6.; } + static T a21() { return (T) 3./6.; } + static T a22() { return (T) 3./6.; } + static T a23() { return (T)- 3./6.; } + static T a30() { return 0; } + static T a31() { return 0; } + static T a32() { return 0; } + static T a33() { return (T) 1./6.; } + }; + + template<class T> + inline T clamp(T a, T lower, T upper) + { + return (a < lower) ? lower : + (a > upper) ? upper : a; + } +} + +template<typename KERNEL> +struct CubicKernel +{ + typedef typename KERNEL KernelInternal; + typedef typename KERNEL::FloatType FloatType; + + static FloatType kernel(FloatType x) + { + x = fabs(x); + if (x >= (FloatType)2.) + return 0.0f; + if (x <= (FloatType)1.) { + FloatType x2 = x * x; + FloatType x3 = x2 * x; + return KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3; + } + assert(x > (FloatType)1. && x < (FloatType)2.); + x -= (FloatType)1.; + FloatType x2 = x * x; + FloatType x3 = x2 * x; + return KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3; + } + + static FloatType interpolate(FloatType f0, FloatType f1, FloatType f2, FloatType f3, FloatType x) + { + const FloatType x2 = x*x; + const FloatType x3 = x*x*x; + return f0*(KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3) + + f1*(KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3) + + f2*(KERNEL::a20() + KERNEL::a21() * x + KERNEL::a22() * x2 + KERNEL::a23() * x3) + + f3*(KERNEL::a30() + KERNEL::a31() * x + KERNEL::a32() * x2 + KERNEL::a33() * x3); + } +}; + +// Linear splines +typedef CubicKernel<BicubicInternal::LinearKernel<float>> LinearKernelf; +typedef CubicKernel<BicubicInternal::LinearKernel<double>> LinearKerneld; +// Catmul-Rom splines +typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<float>> CubicCatmulRomKernelf; +typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<double>> CubicCatmulRomKerneld; +typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<float>> CubicInterpolationKernelf; +typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<double>> CubicInterpolationKerneld; +// Cubic B-splines +typedef CubicKernel<BicubicInternal::CubicBSplineKernel<float>> CubicBSplineKernelf; +typedef CubicKernel<BicubicInternal::CubicBSplineKernel<double>> CubicBSplineKerneld; + +template<typename KERNEL, typename Derived> +static float cubic_interpolate(const Eigen::ArrayBase<Derived> &F, const typename KERNEL::FloatType pt, const typename KERNEL::FloatType dx) +{ + typedef typename KERNEL::FloatType T; + const int w = int(F.size()); + const int ix = (int)floor(pt); + const T s = pt - (T)ix; + + if (ix > 1 && ix + 2 < w) { + // Inside the fully interpolated region. + return KERNEL::interpolate(F[ix - 1], F[ix], F[ix + 1], F[ix + 2], s); + } + // Transition region. Extend with a constant function. + auto f = [&F, w](x) { return F[BicubicInternal::clamp(x, 0, w - 1)]; } + return KERNEL::interpolate(f(ix - 1), f(ix), f(ix + 1), f(ix + 2), s); +} + +template<typename KERNEL, typename Derived> +static float bicubic_interpolate(const Eigen::MatrixBase<Derived> &F, const Eigen::Matrix<typename KERNEL::FloatType, 2, 1, Eigen::DontAlign> &pt, const typename KERNEL::FloatType dx) +{ + typedef typename KERNEL::FloatType T; + const int w = F.cols(); + const int h = F.rows(); + const int ix = (int)floor(pt[0]); + const int iy = (int)floor(pt[1]); + const T s = pt[0] - (T)ix; + const T t = pt[1] - (T)iy; + + if (ix > 1 && ix + 2 < w && iy > 1 && iy + 2 < h) { + // Inside the fully interpolated region. + return KERNEL::interpolate( + KERNEL::interpolate(F(ix-1,iy-1),F(ix ,iy-1),F(ix+1,iy-1),F(ix+2,iy-1),s), + KERNEL::interpolate(F(ix-1,iy ),F(ix ,iy ),F(ix+1,iy ),F(ix+2,iy ),s), + KERNEL::interpolate(F(ix-1,iy+1),F(ix ,iy+1),F(ix+1,iy+1),F(ix+2,iy+1),s), + KERNEL::interpolate(F(ix-1,iy+2),F(ix ,iy+2),F(ix+1,iy+2),F(ix+2,iy+2),s),t); + } + // Transition region. Extend with a constant function. + auto f = [&f, w, h](int x, int y) { return F(BicubicInternal::clamp(x,0,w-1),BicubicInternal::clamp(y,0,h-1)); } + return KERNEL::interpolate( + KERNEL::interpolate(f(ix-1,iy-1),f(ix ,iy-1),f(ix+1,iy-1),f(ix+2,iy-1),s), + KERNEL::interpolate(f(ix-1,iy ),f(ix ,iy ),f(ix+1,iy ),f(ix+2,iy ),s), + KERNEL::interpolate(f(ix-1,iy+1),f(ix ,iy+1),f(ix+1,iy+1),f(ix+2,iy+1),s), + KERNEL::interpolate(f(ix-1,iy+2),f(ix ,iy+2),f(ix+1,iy+2),f(ix+2,iy+2),s),t); +} + +} // namespace Slic3r + +#endif /* BICUBIC_HPP */ |