mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Mirroring refactored.
This commit is contained in:
		
							parent
							
								
									baab5e49f1
								
							
						
					
					
						commit
						bb73b59aa6
					
				
					 10 changed files with 380 additions and 680 deletions
				
			
		|  | @ -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) | ||||
|  |  | |||
|  | @ -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"); | ||||
|  |  | |||
|  | @ -1,349 +0,0 @@ | |||
| #ifndef PRINTEXPORT_HPP | ||||
| #define PRINTEXPORT_HPP | ||||
| 
 | ||||
| // For png export of the sliced model
 | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <boost/log/trivial.hpp> | ||||
| #include <boost/filesystem/path.hpp> | ||||
| 
 | ||||
| #include "Rasterizer/Rasterizer.hpp" | ||||
| //#include <tbb/parallel_for.h>
 | ||||
| //#include <tbb/spin_mutex.h>//#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<FilePrinterFormat format> | ||||
| 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<class T = void> 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 Fmt> class LayerWriter { | ||||
| public: | ||||
| 
 | ||||
|     LayerWriter(const std::string& /*zipfile_path*/) | ||||
|     { | ||||
|         static_assert(VeryFalse<Fmt>::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<class T> 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<FilePrinterFormat::SLA_PNGZIP> | ||||
| { | ||||
|     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<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; | ||||
|     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<class LyrFmt> | ||||
|     inline void save(const std::string& fpath, const std::string& prjname = "") | ||||
|     { | ||||
|         try { | ||||
|             LayerWriter<LyrFmt> 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<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]); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| #endif // PRINTEXPORT_HPP
 | ||||
|  | @ -1,186 +0,0 @@ | |||
| #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 */ | ||||
|  | @ -1,5 +1,8 @@ | |||
| #include "Rasterizer.hpp" | ||||
| #include <ExPolygon.hpp> | ||||
| #ifndef SLARASTER_CPP | ||||
| #define SLARASTER_CPP | ||||
| 
 | ||||
| #include "SLARaster.hpp" | ||||
| #include "libslic3r/ExPolygon.hpp" | ||||
| #include <libnest2d/backends/clipper/clipper_polygon.hpp> | ||||
| 
 | ||||
| // 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<double(double)> m_gammafn; | ||||
|     Origin m_o; | ||||
|     std::array<bool, 2> 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<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), | ||||
|  | @ -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<class P> 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<bool, 2>& 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<std::uint8_t> 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
 | ||||
|  | @ -1,5 +1,5 @@ | |||
| #ifndef RASTERIZER_HPP | ||||
| #define RASTERIZER_HPP | ||||
| #ifndef SLARASTER_HPP | ||||
| #define SLARASTER_HPP | ||||
| 
 | ||||
| #include <ostream> | ||||
| #include <memory> | ||||
|  | @ -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 <class...Args> Raster(Args...args) {  | ||||
|         reset(std::forward<Args>(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<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. | ||||
|  | @ -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
 | ||||
							
								
								
									
										136
									
								
								src/libslic3r/SLA/SLARasterWriter.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/libslic3r/SLA/SLARasterWriter.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 = {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<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
 | ||||
							
								
								
									
										139
									
								
								src/libslic3r/SLA/SLARasterWriter.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								src/libslic3r/SLA/SLARasterWriter.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,139 @@ | |||
| #ifndef SLARASTERWRITER_HPP | ||||
| #define SLARASTERWRITER_HPP | ||||
| 
 | ||||
| // For png export of the sliced model
 | ||||
| #include <fstream> | ||||
| #include <sstream> | ||||
| #include <vector> | ||||
| 
 | ||||
| #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<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; | ||||
|     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<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
 | ||||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -3,11 +3,11 @@ | |||
| 
 | ||||
| #include <mutex> | ||||
| #include "PrintBase.hpp" | ||||
| #include "PrintExport.hpp" | ||||
| //#include "PrintExport.hpp"
 | ||||
| #include "SLA/SLARasterWriter.hpp" | ||||
| #include "Point.hpp" | ||||
| #include "MTUtils.hpp" | ||||
| #include <libnest2d/backends/clipper/clipper_polygon.hpp> | ||||
| #include "Zipper.hpp" | ||||
| 
 | ||||
| namespace Slic3r { | ||||
| 
 | ||||
|  | @ -322,37 +322,6 @@ struct SLAPrintStatistics | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| // The implementation of creating zipped archives with wxWidgets
 | ||||
| template<> class LayerWriter<Zipper> { | ||||
|     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<class T> inline LayerWriter& operator<<(T&& arg) { | ||||
|         m_zip << std::forward<T>(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<class Fmt = Zipper> | ||||
|     inline void export_raster(const std::string& fpath, | ||||
|                        const std::string& projectname = "") | ||||
|                               const std::string& projectname = "") | ||||
|     { | ||||
|         if(m_printer) m_printer->save<Fmt>(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<PrintLayer>& print_layers() const { return m_printer_input; } | ||||
| 
 | ||||
| private: | ||||
|     using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>; | ||||
|     using SLAPrinter = sla::SLARasterWriter; | ||||
|     using SLAPrinterPtr = std::unique_ptr<SLAPrinter>; | ||||
| 
 | ||||
|     // Implement same logic as in SLAPrintObject
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros