From baab5e49f1a95f48f4bafcdd9060b0c6f896cb00 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 18 May 2019 16:56:46 +0200 Subject: [PATCH 1/5] Mirroring parameters prepared for UI. Actual mirroring disabled, it will be refactored to maintain clarity of code. --- src/libslic3r/PrintConfig.cpp | 12 ++++++ src/libslic3r/PrintConfig.hpp | 4 ++ src/libslic3r/PrintExport.hpp | 22 ++++++++++ src/libslic3r/Rasterizer/Rasterizer.hpp | 7 +++- src/libslic3r/SLAPrint.cpp | 54 +++++++++++++------------ 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 87ea26301..bd29916af 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2258,6 +2258,18 @@ void PrintConfigDef::init_sla_params() def->min = 100; def->set_default_value(new ConfigOptionInt(1440)); + def = this->add("display_mirror_x", coBool); + def->full_label = L("Display mirroring in X axis"); + def->label = L("Mirror X"); + def->tooltip = L("Enable mirroring of output images in the X axis"); + def->set_default_value(new ConfigOptionBool(false)); + + def = this->add("display_mirror_y", coBool); + def->full_label = L("Display mirroring in Y axis"); + def->label = L("Mirror Y"); + def->tooltip = L("Enable mirroring of output images in the Y axis"); + def->set_default_value(new ConfigOptionBool(true)); + def = this->add("display_orientation", coEnum); def->label = L("Display orientation"); def->tooltip = L("Set the actual LCD display orientation inside the SLA printer." diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1da22b377..248b89e32 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1083,6 +1083,8 @@ public: ConfigOptionInt display_pixels_x; ConfigOptionInt display_pixels_y; ConfigOptionEnum display_orientation; + ConfigOptionBool display_mirror_x; + ConfigOptionBool display_mirror_y; ConfigOptionFloats relative_correction; ConfigOptionFloat absolute_correction; ConfigOptionFloat gamma_correction; @@ -1099,6 +1101,8 @@ protected: OPT_PTR(display_height); OPT_PTR(display_pixels_x); OPT_PTR(display_pixels_y); + OPT_PTR(display_mirror_x); + OPT_PTR(display_mirror_y); OPT_PTR(display_orientation); OPT_PTR(relative_correction); OPT_PTR(absolute_correction); diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index f6537ed32..c7d462b4f 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -210,6 +210,28 @@ public: { } + inline FilePrinter(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_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; + + auto ro = cfg.display_orientation.getInt(); + + // Here is the trick with the orientation. + m_o = ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT : + Raster::Origin::TOP_LEFT; + + m_gamma = cfg.gamma_correction.getFloat(); + } + FilePrinter(const FilePrinter& ) = delete; FilePrinter(FilePrinter&& m): m_layers_rst(std::move(m.m_layers_rst)), diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp index 3fffe1a36..d338a5e3b 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/src/libslic3r/Rasterizer/Rasterizer.hpp @@ -75,7 +75,10 @@ public: struct Resolution { unsigned width_px; unsigned height_px; - inline Resolution(unsigned w, unsigned h): width_px(w), height_px(h) {} + + inline Resolution(unsigned w = 0, unsigned h = 0): + width_px(w), height_px(h) {} + inline unsigned pixels() const /*noexcept*/ { return width_px * height_px; } @@ -85,7 +88,7 @@ public: struct PixelDim { double w_mm; double h_mm; - inline PixelDim(double px_width_mm, double px_height_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) {} }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 457be23ba..6d2318bea 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1008,7 +1008,7 @@ void SLAPrint::process() namespace sl = libnest2d::shapelike; // For algorithms // If the raster has vertical orientation, we will flip the coordinates - bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait; +// bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait; // Set up custom union and diff functions for clipper polygons auto polyunion = [] (const ClipperPolygons& subjects) @@ -1066,9 +1066,9 @@ void SLAPrint::process() // get polygons for all instances in the object auto get_all_polygons = - [flpXY](const ExPolygons& input_polygons, - const std::vector& instances, - bool is_lefthanded) + [](const ExPolygons& input_polygons, + const std::vector& instances, + bool is_lefthanded) { ClipperPolygons polygons; polygons.reserve(input_polygons.size() * instances.size()); @@ -1082,7 +1082,7 @@ void SLAPrint::process() // We need to reverse if flpXY OR is_lefthanded is true but // not if both are true which is a logical inequality (XOR) - bool needreverse = flpXY != is_lefthanded; + bool needreverse = /*flpXY !=*/ is_lefthanded; // should be a move poly.Contour.reserve(polygon.contour.size() + 1); @@ -1117,10 +1117,10 @@ void SLAPrint::process() sl::translate(poly, ClipperPoint{instances[i].shift(X), instances[i].shift(Y)}); - if (flpXY) { - for(auto& p : poly.Contour) std::swap(p.X, p.Y); - for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y); - } +// if (flpXY) { +// for(auto& p : poly.Contour) std::swap(p.X, p.Y); +// for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y); +// } polygons.emplace_back(std::move(poly)); } @@ -1289,27 +1289,29 @@ void SLAPrint::process() { // create a raster printer for the current print parameters // I don't know any better - auto& ocfg = m_objects.front()->m_config; - auto& matcfg = m_material_config; - auto& printcfg = m_printer_config; +// auto& ocfg = m_objects.front()->m_config; +// auto& matcfg = m_material_config; +// auto& printcfg = m_printer_config; - double w = printcfg.display_width.getFloat(); - double h = printcfg.display_height.getFloat(); - auto pw = unsigned(printcfg.display_pixels_x.getInt()); - auto ph = unsigned(printcfg.display_pixels_y.getInt()); - double lh = ocfg.layer_height.getFloat(); - double exp_t = matcfg.exposure_time.getFloat(); - double iexp_t = matcfg.initial_exposure_time.getFloat(); +// double w = printcfg.display_width.getFloat(); +// double h = printcfg.display_height.getFloat(); +// auto pw = unsigned(printcfg.display_pixels_x.getInt()); +// auto ph = unsigned(printcfg.display_pixels_y.getInt()); +// double lh = ocfg.layer_height.getFloat(); +// double exp_t = matcfg.exposure_time.getFloat(); +// double iexp_t = matcfg.initial_exposure_time.getFloat(); - double gamma = m_printer_config.gamma_correction.getFloat(); +// double gamma = m_printer_config.gamma_correction.getFloat(); - if(flpXY) { std::swap(w, h); std::swap(pw, ph); } +// if(flpXY) { std::swap(w, h); std::swap(pw, ph); } - m_printer.reset( - new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t, - flpXY? SLAPrinter::RO_PORTRAIT : - SLAPrinter::RO_LANDSCAPE, - gamma)); +// m_printer.reset( +// new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t, +// flpXY? SLAPrinter::RO_PORTRAIT : +// SLAPrinter::RO_LANDSCAPE, +// gamma)); + + m_printer.reset(new SLAPrinter(m_printer_config, m_material_config, m_default_object_config.layer_height.getFloat())); } // Allocate space for all the layers From bb73b59aa672632eab7e6f87514cb94688c5fb02 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 18 May 2019 22:45:24 +0200 Subject: [PATCH 2/5] Mirroring refactored. --- src/libslic3r/CMakeLists.txt | 7 +- src/libslic3r/PrintConfig.cpp | 4 +- src/libslic3r/PrintExport.hpp | 349 ------------------ src/libslic3r/Rasterizer/bicubic.h | 186 ---------- .../Rasterizer.cpp => SLA/SLARaster.cpp} | 99 +++-- .../Rasterizer.hpp => SLA/SLARaster.hpp} | 64 ++-- src/libslic3r/SLA/SLARasterWriter.cpp | 136 +++++++ src/libslic3r/SLA/SLARasterWriter.hpp | 139 +++++++ src/libslic3r/SLAPrint.cpp | 34 +- src/libslic3r/SLAPrint.hpp | 42 +-- 10 files changed, 380 insertions(+), 680 deletions(-) delete mode 100644 src/libslic3r/PrintExport.hpp delete mode 100644 src/libslic3r/Rasterizer/bicubic.h rename src/libslic3r/{Rasterizer/Rasterizer.cpp => SLA/SLARaster.cpp} (77%) rename src/libslic3r/{Rasterizer/Rasterizer.hpp => SLA/SLARaster.hpp} (68%) create mode 100644 src/libslic3r/SLA/SLARasterWriter.cpp create mode 100644 src/libslic3r/SLA/SLARasterWriter.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index ce93d95fa..3c32a22ed 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -130,13 +130,10 @@ add_library(libslic3r STATIC Print.hpp PrintBase.cpp PrintBase.hpp - PrintExport.hpp PrintConfig.cpp PrintConfig.hpp PrintObject.cpp PrintRegion.cpp - Rasterizer/Rasterizer.hpp - Rasterizer/Rasterizer.cpp SLAPrint.cpp SLAPrint.hpp SLA/SLAAutoSupports.hpp @@ -173,6 +170,10 @@ add_library(libslic3r STATIC SLA/SLARotfinder.cpp SLA/SLABoostAdapter.hpp SLA/SLASpatIndex.hpp + SLA/SLARaster.hpp + SLA/SLARaster.cpp + SLA/SLARasterWriter.hpp + SLA/SLARasterWriter.cpp ) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index bd29916af..d000c2c2c 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2262,13 +2262,13 @@ void PrintConfigDef::init_sla_params() def->full_label = L("Display mirroring in X axis"); def->label = L("Mirror X"); def->tooltip = L("Enable mirroring of output images in the X axis"); - def->set_default_value(new ConfigOptionBool(false)); + def->set_default_value(new ConfigOptionBool(true)); def = this->add("display_mirror_y", coBool); def->full_label = L("Display mirroring in Y axis"); def->label = L("Mirror Y"); def->tooltip = L("Enable mirroring of output images in the Y axis"); - def->set_default_value(new ConfigOptionBool(true)); + def->set_default_value(new ConfigOptionBool(false)); def = this->add("display_orientation", coEnum); def->label = L("Display orientation"); diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp deleted file mode 100644 index c7d462b4f..000000000 --- a/src/libslic3r/PrintExport.hpp +++ /dev/null @@ -1,349 +0,0 @@ -#ifndef PRINTEXPORT_HPP -#define PRINTEXPORT_HPP - -// For png export of the sliced model -#include -#include -#include - -#include -#include - -#include "Rasterizer/Rasterizer.hpp" -//#include -//#include //#include "tbb/mutex.h" - -namespace Slic3r { - -// Used for addressing parameters of FilePrinter::set_statistics() -enum ePrintStatistics -{ - psUsedMaterial = 0, - psNumFade, - psNumSlow, - psNumFast, - - psCnt -}; - -enum class FilePrinterFormat { - SLA_PNGZIP, - SVG -}; - -/* - * Interface for a file printer of the slices. Implementation can be an SVG - * or PNG printer or any other format. - * - * The format argument specifies the output format of the printer and it enables - * different implementations of this class template for each supported format. - * - */ -template -class FilePrinter { -public: - - // Draw a polygon which is a polygon inside a slice on the specified layer. - void draw_polygon(const ExPolygon& p, unsigned lyr); - void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr); - - // Tell the printer how many layers should it consider. - void layers(unsigned layernum); - - // Get the number of layers in the print. - unsigned layers() const; - - /* Switch to a particular layer. If there where less layers then the - * specified layer number than an appropriate number of layers will be - * allocated in the printer. - */ - void begin_layer(unsigned layer); - - // Allocate a new layer on top of the last and switch to it. - void begin_layer(); - - /* - * Finish the selected layer. It means that no drawing is allowed on that - * layer anymore. This fact can be used to prepare the file system output - * data like png comprimation and so on. - */ - void finish_layer(unsigned layer); - - // Finish the top layer. - void finish_layer(); - - // Save all the layers into the file (or dir) specified in the path argument - // An optional project name can be added to be used for the layer file names - void save(const std::string& path, const std::string& projectname = ""); - - // Save only the selected layer to the file specified in path argument. - void save_layer(unsigned lyr, const std::string& path); -}; - -// Provokes static_assert in the right way. -template struct VeryFalse { static const bool value = false; }; - -// This can be explicitly implemented in the gui layer or the default Zipper -// API in libslic3r with minz. -template class LayerWriter { -public: - - LayerWriter(const std::string& /*zipfile_path*/) - { - static_assert(VeryFalse::value, - "No layer writer implementation provided!"); - } - - // Should create a new file within the zip with the given filename. It - // should also finish any previous entry. - void next_entry(const std::string& /*fname*/) {} - - // Should create a new file within the archive and write the provided data. - void binary_entry(const std::string& /*fname*/, - const std::uint8_t* buf, size_t len); - - // Test whether the object can still be used for writing. - bool is_ok() { return false; } - - // Write some data (text) into the current file (entry) within the archive. - template LayerWriter& operator<<(T&& /*arg*/) { - return *this; - } - - // Flush the current entry into the archive. - void finalize() {} -}; - -// 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. -template<> class FilePrinter -{ - struct Layer { - Raster raster; - RawBytes rawbytes; - - Layer() {} - - Layer(const Layer&) = delete; - Layer(Layer&& m): - raster(std::move(m.raster)) {} - }; - - // We will save the compressed PNG data into stringstreams which can be done - // in parallel. Later we can write every layer to the disk sequentially. - std::vector 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; - Raster::Origin m_o = Raster::Origin::TOP_LEFT; - 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) { - using std::string; - using std::to_string; - - auto expt_str = to_string(m_exp_time_s); - auto expt_first_str = to_string(m_exp_time_first_s); - auto layerh_str = to_string(m_layer_height); - - const std::string cnt_fade_layers = to_string(m_cnt_fade_layers); - const std::string cnt_slow_layers = to_string(m_cnt_slow_layers); - const std::string cnt_fast_layers = to_string(m_cnt_fast_layers); - const std::string used_material = to_string(m_used_material); - - return 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"; - } - -public: - - enum RasterOrientation { - RO_LANDSCAPE, - RO_PORTRAIT - }; - - // We will play with the raster's coordinate origin parameter. When the - // printer should print in landscape mode it should have the Y axis flipped - // because the layers should be displayed upside down. PNG has its - // coordinate origin in the top-left corner so normally the Raster objects - // should be instantiated with the TOP_LEFT flag. However, in landscape mode - // we do want the pictures to be upside down so we will make BOTTOM_LEFT - // type rasters and the PNG format will do the flipping automatically. - - // In case of portrait images, we have to rotate the image by a 90 degrees - // and flip the y axis. To get the correct upside-down orientation of the - // slice images, we can flip the x and y coordinates of the input polygons - // and do the Y flipping of the image. This will generate the correct - // orientation in portrait mode. - - inline FilePrinter(double width_mm, double height_mm, - unsigned width_px, unsigned height_px, - double layer_height, - double exp_time, double exp_time_first, - RasterOrientation ro = RO_PORTRAIT, - double gamma = 1.0): - m_res(width_px, height_px), - m_pxdim(width_mm/width_px, height_mm/height_px), - m_exp_time_s(exp_time), - m_exp_time_first_s(exp_time_first), - m_layer_height(layer_height), - - // Here is the trick with the orientation. - m_o(ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT : - Raster::Origin::TOP_LEFT ), - m_gamma(gamma) - { - } - - inline FilePrinter(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_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; - - auto ro = cfg.display_orientation.getInt(); - - // Here is the trick with the orientation. - m_o = ro == RO_LANDSCAPE? Raster::Origin::BOTTOM_LEFT : - Raster::Origin::TOP_LEFT; - - m_gamma = cfg.gamma_correction.getFloat(); - } - - FilePrinter(const FilePrinter& ) = delete; - FilePrinter(FilePrinter&& m): - m_layers_rst(std::move(m.m_layers_rst)), - m_res(m.m_res), - m_pxdim(m.m_pxdim) {} - - inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } - inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - - inline void draw_polygon(const ExPolygon& p, unsigned lyr) { - assert(lyr < m_layers_rst.size()); - m_layers_rst[lyr].raster.draw(p); - } - - inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) { - assert(lyr < m_layers_rst.size()); - 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_o, m_gamma); - } - - inline void begin_layer() { - m_layers_rst.emplace_back(); - m_layers_rst.front().raster.reset(m_res, m_pxdim, m_o, 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::Compression::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::Compression::PNG); - m_layers_rst.back().raster.reset(); - } - } - - template - inline void save(const std::string& fpath, const std::string& prjname = "") - { - try { - LayerWriter writer(fpath); - if(!writer.is_ok()) return; - - std::string project = prjname.empty()? - boost::filesystem::path(fpath).stem().string() : prjname; - - writer.next_entry("config.ini"); - if(!writer.is_ok()) return; - - writer << createIniContent(project); - - for(unsigned i = 0; i < m_layers_rst.size() && writer.is_ok(); i++) - { - if(m_layers_rst[i].rawbytes.size() > 0) { - char lyrnum[6]; - std::sprintf(lyrnum, "%.5d", i); - auto zfilename = project + lyrnum + ".png"; - if(!writer.is_ok()) break; - - writer.binary_entry(zfilename, - m_layers_rst[i].rawbytes.data(), - m_layers_rst[i].rawbytes.size()); - } - } - - writer.finalize(); - } catch(std::exception& e) { - BOOST_LOG_TRIVIAL(error) << e.what(); - // Rethrow the exception - throw; - } - } - - void save_layer(unsigned lyr, const std::string& path) { - unsigned i = lyr; - assert(i < m_layers_rst.size()); - - char lyrnum[6]; - std::sprintf(lyrnum, "%.5d", lyr); - std::string loc = path + "layer" + lyrnum + ".png"; - - std::fstream out(loc, std::fstream::out | std::fstream::binary); - if(out.good()) { - m_layers_rst[i].raster.save(out, Raster::Compression::PNG); - } else { - BOOST_LOG_TRIVIAL(error) << "Can't create file for layer"; - } - - out.close(); - m_layers_rst[i].raster.reset(); - } - - void set_statistics(const std::vector 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]); - } -}; - -} - -#endif // PRINTEXPORT_HPP diff --git a/src/libslic3r/Rasterizer/bicubic.h b/src/libslic3r/Rasterizer/bicubic.h deleted file mode 100644 index 870d00dbd..000000000 --- a/src/libslic3r/Rasterizer/bicubic.h +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef BICUBIC_HPP -#define BICUBIC_HPP - -#include -#include -#include - -#include - -namespace Slic3r { - -namespace BicubicInternal { - // Linear kernel, to be able to test cubic methods with hat kernels. - template - 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 - 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 - 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 - inline T clamp(T a, T lower, T upper) - { - return (a < lower) ? lower : - (a > upper) ? upper : a; - } -} - -template -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> LinearKernelf; -typedef CubicKernel> LinearKerneld; -// Catmul-Rom splines -typedef CubicKernel> CubicCatmulRomKernelf; -typedef CubicKernel> CubicCatmulRomKerneld; -typedef CubicKernel> CubicInterpolationKernelf; -typedef CubicKernel> CubicInterpolationKerneld; -// Cubic B-splines -typedef CubicKernel> CubicBSplineKernelf; -typedef CubicKernel> CubicBSplineKerneld; - -template -static float cubic_interpolate(const Eigen::ArrayBase &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 -static float bicubic_interpolate(const Eigen::MatrixBase &F, const Eigen::Matrix &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 */ diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/SLA/SLARaster.cpp similarity index 77% rename from src/libslic3r/Rasterizer/Rasterizer.cpp rename to src/libslic3r/SLA/SLARaster.cpp index 6384a241f..fba1f46f3 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.cpp +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -1,5 +1,8 @@ -#include "Rasterizer.hpp" -#include +#ifndef SLARASTER_CPP +#define SLARASTER_CPP + +#include "SLARaster.hpp" +#include "libslic3r/ExPolygon.hpp" #include // For rasterizing @@ -19,11 +22,13 @@ namespace Slic3r { -const Polygon& contour(const ExPolygon& p) { return p.contour; } -const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; } +inline const Polygon& contour(const ExPolygon& p) { return p.contour; } +inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; } -const Polygons& holes(const ExPolygon& p) { return p.holes; } -const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; } +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: @@ -39,7 +44,7 @@ public: static const TPixel ColorWhite; static const TPixel ColorBlack; - using Origin = Raster::Origin; + using Format = Raster::Format; private: Raster::Resolution m_resolution; @@ -52,16 +57,20 @@ private: TRendererAA m_renderer; std::function m_gammafn; - Origin m_o; + std::array m_mirror; 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, - Origin o, double gamma = 1.0): + const std::array& mirror, double gamma = 1.0): m_resolution(res), // m_pxdim(pd), m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm), @@ -72,7 +81,7 @@ public: m_pixfmt(m_rbuf), m_raw_renderer(m_pixfmt), m_renderer(m_raw_renderer), - m_o(o) + m_mirror(mirror) { m_renderer.color(ColorWhite); @@ -81,6 +90,18 @@ public: 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; + } + } template void draw(const P &poly) { agg::rasterizer_scanline_aa<> ras; @@ -89,14 +110,16 @@ public: ras.gamma(m_gammafn); auto&& path = to_path(contour(poly)); - - if(m_o == Origin::TOP_LEFT) flipy(path); + + 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_o == Origin::TOP_LEFT) flipy(holepath); + if(m_mirror[X]) flipx(holepath); + if(m_mirror[Y]) flipy(holepath); ras.add_path(holepath); } @@ -110,9 +133,7 @@ public: inline TBuffer& buffer() { return m_buf; } inline const Raster::Resolution resolution() { return m_resolution; } - - inline Origin origin() const /*noexcept*/ { return m_o; } - + private: inline double getPx(const Point& p) { return p(0) * m_pxdim_scaled.w_mm; @@ -154,30 +175,23 @@ private: const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); -Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o, double g): - m_impl(new Impl(r, pd, o, g)) {} +Raster::Raster() = default; +Raster::~Raster() = default; +Raster::Raster(Raster &&m) = default; +Raster& Raster::operator=(Raster&&) = default; -Raster::Raster() {} - -Raster::~Raster() {} - -Raster::Raster(Raster &&m): - m_impl(std::move(m.m_impl)) {} - -void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, - double g) +void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, + Format fmt, double gamma) { - // Free up the unnecessary memory and make sure it stays clear after - // an exception - auto o = m_impl? m_impl->origin() : Origin::TOP_LEFT; - reset(r, pd, o, g); + m_impl.reset(); + m_impl.reset(new Impl(r, pd, fmt, gamma)); } void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd, - Raster::Origin o, double gamma) + const std::array& mirror, double gamma) { m_impl.reset(); - m_impl.reset(new Impl(r, pd, o, gamma)); + m_impl.reset(new Impl(r, pd, mirror, gamma)); } void Raster::reset() @@ -208,13 +222,13 @@ void Raster::draw(const ClipperLib::Polygon &poly) m_impl->draw(poly); } -void Raster::save(std::ostream& stream, Compression comp) +void Raster::save(std::ostream& stream, Format fmt) { assert(m_impl); if(!stream.good()) return; - switch(comp) { - case Compression::PNG: { + 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( @@ -231,7 +245,7 @@ void Raster::save(std::ostream& stream, Compression comp) break; } - case Compression::RAW: { + case Format::RAW: { stream << "P5 " << m_impl->resolution().width_px << " " << m_impl->resolution().height_px << " " @@ -244,14 +258,14 @@ void Raster::save(std::ostream& stream, Compression comp) } } -RawBytes Raster::save(Raster::Compression comp) +RawBytes Raster::save(Format fmt) { assert(m_impl); std::vector data; size_t s = 0; - switch(comp) { - case Compression::PNG: { + switch(fmt) { + case Format::PNG: { void *rawdata = tdefl_write_image_to_png_file_in_memory( m_impl->buffer().data(), int(resolution().width_px), @@ -265,7 +279,7 @@ RawBytes Raster::save(Raster::Compression comp) MZ_FREE(rawdata); break; } - case Compression::RAW: { + 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 "; @@ -287,3 +301,6 @@ RawBytes Raster::save(Raster::Compression comp) } } +} + +#endif // SLARASTER_CPP diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/SLA/SLARaster.hpp similarity index 68% rename from src/libslic3r/Rasterizer/Rasterizer.hpp rename to src/libslic3r/SLA/SLARaster.hpp index d338a5e3b..03fb06d6c 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -1,5 +1,5 @@ -#ifndef RASTERIZER_HPP -#define RASTERIZER_HPP +#ifndef SLARASTER_HPP +#define SLARASTER_HPP #include #include @@ -8,10 +8,12 @@ namespace ClipperLib { struct Polygon; } -namespace Slic3r { +namespace Slic3r { class ExPolygon; +namespace sla { + // Raw byte buffer paired with its size. Suitable for compressed PNG data. class RawBytes { @@ -23,19 +25,24 @@ public: size_t size() const { return m_buffer.size(); } const uint8_t * data() { return m_buffer.data(); } + + RawBytes(const RawBytes&) = delete; + RawBytes(RawBytes&&) = default; + RawBytes& operator=(const RawBytes&) = delete; + RawBytes& operator=(RawBytes&&) = default; // ///////////////////////////////////////////////////////////////////////// // FIXME: the following is needed for MSVC2013 compatibility // ///////////////////////////////////////////////////////////////////////// - RawBytes(const RawBytes&) = delete; - RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} +// RawBytes(const RawBytes&) = delete; +// RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} - RawBytes& operator=(const RawBytes&) = delete; - RawBytes& operator=(RawBytes&& mv) { - m_buffer = std::move(mv.m_buffer); - return *this; - } +// RawBytes& operator=(const RawBytes&) = delete; +// RawBytes& operator=(RawBytes&& mv) { +// m_buffer = std::move(mv.m_buffer); +// return *this; +// } // ///////////////////////////////////////////////////////////////////////// }; @@ -54,23 +61,11 @@ class Raster { public: /// Supported compression types - enum class Compression { + enum class Format { RAW, //!> Uncompressed pixel data PNG //!> PNG compression }; - /// The Rasterizer expects the input polygons to have their coordinate - /// system origin in the bottom left corner. If the raster is then - /// configured with the TOP_LEFT origin parameter (in the constructor) than - /// it will flip the Y axis in output to maintain the correct orientation. - /// This is the default case with PNG images. They have the origin in the - /// top left corner. Without the flipping, the image would be upside down - /// with the scaled (clipper) coordinate system of the input polygons. - enum class Origin { - TOP_LEFT, - BOTTOM_LEFT - }; - /// Type that represents a resolution in pixels. struct Resolution { unsigned width_px; @@ -93,19 +88,21 @@ public: }; /// Constructor taking the resolution and the pixel dimension. - Raster(const Resolution& r, const PixelDim& pd, - Origin o = Origin::BOTTOM_LEFT, double gamma = 1.0); + template Raster(Args...args) { + reset(std::forward(args)...); + } Raster(); 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. - void reset(const Resolution& r, const PixelDim& pd, double gamma = 1.0); - void reset(const Resolution& r, const PixelDim& pd, Origin o, double gamma); - + void reset(const Resolution&, const PixelDim&, const std::array& 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. @@ -123,10 +120,13 @@ public: void draw(const ClipperLib::Polygon& poly); /// Save the raster on the specified stream. - void save(std::ostream& stream, Compression comp = Compression::RAW); + void save(std::ostream& stream, Format = Format::PNG); - RawBytes save(Compression comp = Compression::RAW); + /// Save into a continuous byte stream which is returned. + RawBytes save(Format fmt = Format::PNG); }; -} -#endif // RASTERIZER_HPP +} // 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..b2fe0c72c --- /dev/null +++ b/src/libslic3r/SLA/SLARasterWriter.cpp @@ -0,0 +1,136 @@ +#include "SLARasterWriter.hpp" +#include "libslic3r/Zipper.hpp" +#include "ExPolygon.hpp" +#include + +#include +#include + +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 = {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 = {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 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..9fc23840e --- /dev/null +++ b/src/libslic3r/SLA/SLARasterWriter.hpp @@ -0,0 +1,139 @@ +#ifndef SLARASTERWRITER_HPP +#define SLARASTERWRITER_HPP + +// For png export of the sliced model +#include +#include +#include + +#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; + + Layer(Layer&& m) = default; + Layer& operator=(Layer&&) = default; + }; + + // 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 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 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; + SLARasterWriter(SLARasterWriter&& m) = default; + SLARasterWriter& operator=(SLARasterWriter&&) = default; +// SLARasterWriter(SLARasterWriter&& m) = default; +// SLARasterWriter(SLARasterWriter&& m): +// m_layers_rst(std::move(m.m_layers_rst)), +// m_res(m.m_res), +// m_pxdim(m.m_pxdim) {} + + inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } + inline unsigned layers() const { return unsigned(m_layers_rst.size()); } + + template 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 statistics); +}; + +} // namespace sla +} // namespace Slic3r + +#endif // SLARASTERWRITER_HPP diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 6d2318bea..fba974822 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1281,37 +1281,11 @@ void SLAPrint::process() auto rasterize = [this, max_objstatus]() { if(canceled()) return; - // collect all the keys - - // If the raster has vertical orientation, we will flip the coordinates - bool flpXY = m_printer_config.display_orientation.getInt() == - SLADisplayOrientation::sladoPortrait; - { // create a raster printer for the current print parameters - // I don't know any better -// auto& ocfg = m_objects.front()->m_config; -// auto& matcfg = m_material_config; -// auto& printcfg = m_printer_config; - -// double w = printcfg.display_width.getFloat(); -// double h = printcfg.display_height.getFloat(); -// auto pw = unsigned(printcfg.display_pixels_x.getInt()); -// auto ph = unsigned(printcfg.display_pixels_y.getInt()); -// double lh = ocfg.layer_height.getFloat(); -// double exp_t = matcfg.exposure_time.getFloat(); -// double iexp_t = matcfg.initial_exposure_time.getFloat(); - -// double gamma = m_printer_config.gamma_correction.getFloat(); - -// if(flpXY) { std::swap(w, h); std::swap(pw, ph); } - -// m_printer.reset( -// new SLAPrinter(w, h, pw, ph, lh, exp_t, iexp_t, -// flpXY? SLAPrinter::RO_PORTRAIT : -// SLAPrinter::RO_LANDSCAPE, -// gamma)); - - m_printer.reset(new SLAPrinter(m_printer_config, m_material_config, m_default_object_config.layer_height.getFloat())); + double layerh = m_default_object_config.layer_height.getFloat(); + m_printer.reset(new SLAPrinter(m_printer_config, + m_material_config, + layerh)); } // Allocate space for all the layers diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index dea468e7a..1ad40ef15 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -3,11 +3,11 @@ #include #include "PrintBase.hpp" -#include "PrintExport.hpp" +//#include "PrintExport.hpp" +#include "SLA/SLARasterWriter.hpp" #include "Point.hpp" #include "MTUtils.hpp" #include -#include "Zipper.hpp" namespace Slic3r { @@ -322,37 +322,6 @@ struct SLAPrintStatistics } }; -// The implementation of creating zipped archives with wxWidgets -template<> class LayerWriter { - Zipper m_zip; -public: - - LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {} - - void next_entry(const std::string& fname) { m_zip.add_entry(fname); } - - void binary_entry(const std::string& fname, - const std::uint8_t* buf, - size_t l) - { - m_zip.add_entry(fname, buf, l); - } - - template inline LayerWriter& operator<<(T&& arg) { - m_zip << std::forward(arg); return *this; - } - - bool is_ok() const { - return true; // m_zip blows up if something goes wrong... - } - - // After finalize, no writing to the archive will have an effect. The only - // valid operation is to dispose the object calling the destructor which - // should close the file. This method can throw and signal potential errors - // when flushing the archive. This is why its present. - void finalize() { m_zip.finalize(); } -}; - /** * @brief This class is the high level FSM for the SLA printing process. * @@ -385,11 +354,10 @@ public: // Returns true if the last step was finished with success. bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); } - template inline void export_raster(const std::string& fpath, - const std::string& projectname = "") + const std::string& projectname = "") { - if(m_printer) m_printer->save(fpath, projectname); + if(m_printer) m_printer->save(fpath, projectname); } const PrintObjects& objects() const { return m_objects; } @@ -450,7 +418,7 @@ public: const std::vector& print_layers() const { return m_printer_input; } private: - using SLAPrinter = FilePrinter; + using SLAPrinter = sla::SLARasterWriter; using SLAPrinterPtr = std::unique_ptr; // Implement same logic as in SLAPrintObject From 38d54d779a1b5ca2d7bd992ec5920d6963f94420 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Sat, 18 May 2019 23:21:59 +0200 Subject: [PATCH 3/5] Mirror checkboxes added to the UI. Everything seems to work fine. --- src/libslic3r/PrintConfig.cpp | 14 ++++++++------ src/libslic3r/SLA/SLARaster.cpp | 14 ++++++++++++++ src/libslic3r/SLA/SLARaster.hpp | 23 +++++++++++++++++++---- src/libslic3r/SLAPrint.cpp | 2 ++ src/slic3r/GUI/Preset.cpp | 1 + src/slic3r/GUI/Tab.cpp | 4 ++++ 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d000c2c2c..89e21934a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2259,15 +2259,17 @@ void PrintConfigDef::init_sla_params() def->set_default_value(new ConfigOptionInt(1440)); def = this->add("display_mirror_x", coBool); - def->full_label = L("Display mirroring in X axis"); - def->label = L("Mirror X"); - def->tooltip = L("Enable mirroring of output images in the X axis"); + def->full_label = L("Display horizontal mirroring"); + def->label = L("Mirror horizontally"); + def->tooltip = L("Enable horizontal mirroring of output images"); + def->mode = comExpert; def->set_default_value(new ConfigOptionBool(true)); def = this->add("display_mirror_y", coBool); - def->full_label = L("Display mirroring in Y axis"); - def->label = L("Mirror Y"); - def->tooltip = L("Enable mirroring of output images in the Y axis"); + def->full_label = L("Display vertical mirroring"); + def->label = L("Mirror vertically"); + def->tooltip = L("Enable vertical mirroring of output images"); + def->mode = comExpert; def->set_default_value(new ConfigOptionBool(false)); def = this->add("display_orientation", coEnum); diff --git a/src/libslic3r/SLA/SLARaster.cpp b/src/libslic3r/SLA/SLARaster.cpp index fba1f46f3..f1b1c8c42 100644 --- a/src/libslic3r/SLA/SLARaster.cpp +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -58,6 +58,7 @@ private: std::function m_gammafn; std::array m_mirror; + Format m_fmt = Format::PNG; inline void flipy(agg::path_storage& path) const { path.flip_y(0, m_resolution.height_px); @@ -101,6 +102,7 @@ public: case Format::PNG: m_mirror = {false, true}; break; case Format::RAW: m_mirror = {false, false}; break; } + m_fmt = fmt; } template void draw(const P &poly) { @@ -131,6 +133,8 @@ public: } inline TBuffer& buffer() { return m_buf; } + + inline Format format() const { return m_fmt; } inline const Raster::Resolution resolution() { return m_resolution; } @@ -258,6 +262,11 @@ void Raster::save(std::ostream& stream, Format fmt) } } +void Raster::save(std::ostream &stream) +{ + save(stream, m_impl->format()); +} + RawBytes Raster::save(Format fmt) { assert(m_impl); @@ -300,6 +309,11 @@ RawBytes Raster::save(Format fmt) return {std::move(data)}; } +RawBytes Raster::save() +{ + return save(m_impl->format()); +} + } } diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/SLARaster.hpp index 03fb06d6c..5051498c5 100644 --- a/src/libslic3r/SLA/SLARaster.hpp +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -100,8 +100,17 @@ public: ~Raster(); /// Reallocated everything for the given resolution and pixel dimension. - void reset(const Resolution&, const PixelDim&, const std::array& mirror, double gamma = 1.0); - void reset(const Resolution& r, const PixelDim& pd, Format o, double gamma = 1.0); + /// 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& 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 @@ -119,11 +128,17 @@ public: 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 = Format::PNG); + void save(std::ostream& stream, Format); + void save(std::ostream& stream); /// Save into a continuous byte stream which is returned. - RawBytes save(Format fmt = Format::PNG); + RawBytes save(Format fmt); + RawBytes save(); }; } // sla diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index fba974822..13df2fa79 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1461,6 +1461,8 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector& Preset::sla_printer_options() "printer_technology", "bed_shape", "max_print_height", "display_width", "display_height", "display_pixels_x", "display_pixels_y", + "display_mirror_x", "display_mirror_y", "display_orientation", "fast_tilt_time", "slow_tilt_time", "area_fill", "relative_correction", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b6bdb7a4b..8fa34e37a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2056,6 +2056,10 @@ void TabPrinter::build_sla() line.append_option(optgroup->get_option("display_pixels_y")); optgroup->append_line(line); optgroup->append_single_option_line("display_orientation"); + + // FIXME: This should be on one line in the UI + optgroup->append_single_option_line("display_mirror_x"); + optgroup->append_single_option_line("display_mirror_y"); optgroup = page->new_optgroup(_(L("Tilt"))); line = { _(L("Tilt time")), "" }; From 4e2ef09a506409697cc5f1f9340e2bf04343ede7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 May 2019 11:19:43 +0200 Subject: [PATCH 4/5] Fixing build on Win and OSX --- src/libslic3r/SLA/SLARaster.cpp | 15 +++++++-- src/libslic3r/SLA/SLARaster.hpp | 23 ++++++++------ src/libslic3r/SLA/SLARasterWriter.cpp | 4 +-- src/libslic3r/SLA/SLARasterWriter.hpp | 46 +++++++++++++++++++++------ src/libslic3r/SLAPrint.cpp | 2 +- 5 files changed, 65 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/SLA/SLARaster.cpp b/src/libslic3r/SLA/SLARaster.cpp index f1b1c8c42..20891c3d4 100644 --- a/src/libslic3r/SLA/SLARaster.cpp +++ b/src/libslic3r/SLA/SLARaster.cpp @@ -1,6 +1,8 @@ #ifndef SLARASTER_CPP #define SLARASTER_CPP +#include + #include "SLARaster.hpp" #include "libslic3r/ExPolygon.hpp" #include @@ -179,10 +181,17 @@ private: const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255); const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0); -Raster::Raster() = default; +template<> Raster::Raster() { reset(); }; Raster::~Raster() = default; -Raster::Raster(Raster &&m) = default; -Raster& Raster::operator=(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) diff --git a/src/libslic3r/SLA/SLARaster.hpp b/src/libslic3r/SLA/SLARaster.hpp index 5051498c5..d3bd52d92 100644 --- a/src/libslic3r/SLA/SLARaster.hpp +++ b/src/libslic3r/SLA/SLARaster.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include namespace ClipperLib { struct Polygon; } @@ -27,22 +29,20 @@ public: const uint8_t * data() { return m_buffer.data(); } RawBytes(const RawBytes&) = delete; - RawBytes(RawBytes&&) = default; RawBytes& operator=(const RawBytes&) = delete; - RawBytes& operator=(RawBytes&&) = default; // ///////////////////////////////////////////////////////////////////////// // FIXME: the following is needed for MSVC2013 compatibility // ///////////////////////////////////////////////////////////////////////// -// RawBytes(const RawBytes&) = delete; -// RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} + // RawBytes(RawBytes&&) = default; + // RawBytes& operator=(RawBytes&&) = default; -// RawBytes& operator=(const RawBytes&) = delete; -// RawBytes& operator=(RawBytes&& mv) { -// m_buffer = std::move(mv.m_buffer); -// return *this; -// } + RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {} + RawBytes& operator=(RawBytes&& mv) { + m_buffer = std::move(mv.m_buffer); + return *this; + } // ///////////////////////////////////////////////////////////////////////// }; @@ -92,7 +92,6 @@ public: reset(std::forward(args)...); } - Raster(); Raster(const Raster& cpy) = delete; Raster& operator=(const Raster& cpy) = delete; Raster(Raster&& m); @@ -141,6 +140,10 @@ public: RawBytes save(); }; +// This prevents the duplicate default constructor warning on MSVC2013 +template<> Raster::Raster(); + + } // sla } // Slic3r diff --git a/src/libslic3r/SLA/SLARasterWriter.cpp b/src/libslic3r/SLA/SLARasterWriter.cpp index b2fe0c72c..f7c3925ac 100644 --- a/src/libslic3r/SLA/SLARasterWriter.cpp +++ b/src/libslic3r/SLA/SLARasterWriter.cpp @@ -44,11 +44,11 @@ void SLARasterWriter::flpXY(ClipperLib::Polygon &poly) void SLARasterWriter::flpXY(ExPolygon &poly) { - for(auto& p : poly.contour.points) p = {p.y(), p.x()}; + 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 = {p.y(), p.x()}; + for(auto& p : h.points) p = Point(p.y(), p.x()); std::reverse(h.points.begin(), h.points.end()); } } diff --git a/src/libslic3r/SLA/SLARasterWriter.hpp b/src/libslic3r/SLA/SLARasterWriter.hpp index 9fc23840e..7133d2dde 100644 --- a/src/libslic3r/SLA/SLARasterWriter.hpp +++ b/src/libslic3r/SLA/SLARasterWriter.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "libslic3r/PrintConfig.hpp" @@ -49,8 +50,18 @@ private: Layer(const Layer&) = delete; // The image is big, do not copy by accident Layer& operator=(const Layer&) = delete; - Layer(Layer&& m) = default; - Layer& operator=(Layer&&) = default; + // ///////////////////////////////////////////////////////////////////// + // 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 @@ -83,13 +94,30 @@ public: SLARasterWriter(const SLARasterWriter& ) = delete; SLARasterWriter& operator=(const SLARasterWriter&) = delete; - SLARasterWriter(SLARasterWriter&& m) = default; - SLARasterWriter& operator=(SLARasterWriter&&) = default; -// SLARasterWriter(SLARasterWriter&& m) = default; -// SLARasterWriter(SLARasterWriter&& m): -// m_layers_rst(std::move(m.m_layers_rst)), -// m_res(m.m_res), -// m_pxdim(m.m_pxdim) {} + + // ///////////////////////////////////////////////////////////////////////// + // 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()); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 13df2fa79..d07eba2b8 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -742,7 +742,7 @@ void SLAPrint::process() // We apply the printer correction offset here. if(clpr_offs != 0) po.m_model_slices[id] = - offset_ex(po.m_model_slices[id], clpr_offs); + offset_ex(po.m_model_slices[id], float(clpr_offs)); mit->set_model_slice_idx(po, id); ++mit; } From 12797f2aa8a3d663b2c07a98d2194603140daade Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 5 Jun 2019 15:50:27 +0200 Subject: [PATCH 5/5] Re-adding missing file --- src/libslic3r/SLA/bicubic.h | 186 ++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 src/libslic3r/SLA/bicubic.h 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 +#include +#include + +#include + +namespace Slic3r { + +namespace BicubicInternal { + // Linear kernel, to be able to test cubic methods with hat kernels. + template + 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 + 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 + 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 + inline T clamp(T a, T lower, T upper) + { + return (a < lower) ? lower : + (a > upper) ? upper : a; + } +} + +template +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> LinearKernelf; +typedef CubicKernel> LinearKerneld; +// Catmul-Rom splines +typedef CubicKernel> CubicCatmulRomKernelf; +typedef CubicKernel> CubicCatmulRomKerneld; +typedef CubicKernel> CubicInterpolationKernelf; +typedef CubicKernel> CubicInterpolationKerneld; +// Cubic B-splines +typedef CubicKernel> CubicBSplineKernelf; +typedef CubicKernel> CubicBSplineKerneld; + +template +static float cubic_interpolate(const Eigen::ArrayBase &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 +static float bicubic_interpolate(const Eigen::MatrixBase &F, const Eigen::Matrix &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 */