2018-06-27 15:43:54 +00:00
|
|
|
#ifndef PRINTEXPORT_HPP
|
|
|
|
#define PRINTEXPORT_HPP
|
|
|
|
|
|
|
|
// For png export of the sliced model
|
|
|
|
#include <fstream>
|
|
|
|
#include <sstream>
|
2018-11-13 16:33:03 +00:00
|
|
|
#include <vector>
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
#include <boost/log/trivial.hpp>
|
|
|
|
|
|
|
|
#include "Rasterizer/Rasterizer.hpp"
|
2018-11-13 16:33:03 +00:00
|
|
|
//#include <tbb/parallel_for.h>
|
|
|
|
//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
|
|
|
|
enum class FilePrinterFormat {
|
2018-11-13 16:33:03 +00:00
|
|
|
SLA_PNGZIP,
|
2018-06-27 15:43:54 +00:00
|
|
|
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.
|
|
|
|
*
|
|
|
|
*/
|
2018-11-13 16:33:03 +00:00
|
|
|
template<FilePrinterFormat format>
|
2018-06-27 15:43:54 +00:00
|
|
|
class FilePrinter {
|
|
|
|
public:
|
|
|
|
|
|
|
|
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
|
2018-09-19 12:54:37 +00:00
|
|
|
void draw_polygon(const ExPolygon& p, unsigned lyr);
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
// 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.
|
|
|
|
*/
|
2018-09-19 12:54:37 +00:00
|
|
|
void begin_layer(unsigned layer);
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
// Allocate a new layer on top of the last and switch to it.
|
2018-09-19 12:54:37 +00:00
|
|
|
void begin_layer();
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-09-19 12:54:37 +00:00
|
|
|
void finish_layer(unsigned layer);
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
// Finish the top layer.
|
2018-09-19 12:54:37 +00:00
|
|
|
void finish_layer();
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
// Save all the layers into the file (or dir) specified in the path argument
|
|
|
|
void save(const std::string& path);
|
|
|
|
|
|
|
|
// Save only the selected layer to the file specified in path argument.
|
2018-09-19 12:54:37 +00:00
|
|
|
void save_layer(unsigned lyr, const std::string& path);
|
2018-06-27 15:43:54 +00:00
|
|
|
};
|
|
|
|
|
2018-11-13 16:33:03 +00:00
|
|
|
// Provokes static_assert in the right way.
|
2018-09-18 17:13:56 +00:00
|
|
|
template<class T = void> struct VeryFalse { static const bool value = false; };
|
|
|
|
|
|
|
|
// This has to be explicitly implemented in the gui layer or a default zlib
|
2018-11-13 16:33:03 +00:00
|
|
|
// based implementation is needed. I don't have time for that and I'm delegating
|
|
|
|
// the implementation to the gui layer where the gui toolkit can cover this.
|
|
|
|
template<class Fmt> class LayerWriter {
|
2018-09-18 17:13:56 +00:00
|
|
|
public:
|
|
|
|
|
2018-09-19 11:43:15 +00:00
|
|
|
LayerWriter(const std::string& /*zipfile_path*/) {
|
2018-11-13 16:33:03 +00:00
|
|
|
static_assert(VeryFalse<Fmt>::value,
|
2018-09-19 11:43:15 +00:00
|
|
|
"No layer writer implementation provided!");
|
2018-09-18 17:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void next_entry(const std::string& /*fname*/) {}
|
|
|
|
|
|
|
|
std::string get_name() { return ""; }
|
|
|
|
|
2018-09-19 11:43:15 +00:00
|
|
|
bool is_ok() { return false; }
|
|
|
|
|
|
|
|
template<class T> LayerWriter& operator<<(const T& /*arg*/) {
|
2018-09-18 17:13:56 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void close() {}
|
|
|
|
};
|
|
|
|
|
2018-06-27 15:43:54 +00:00
|
|
|
// Implementation for PNG raster output
|
|
|
|
// Be aware that if a large number of layers are allocated, it can very well
|
2018-06-28 11:21:24 +00:00
|
|
|
// exhaust the available memory especially on 32 bit platform.
|
2018-11-13 16:33:03 +00:00
|
|
|
template<> class FilePrinter<FilePrinterFormat::SLA_PNGZIP>
|
|
|
|
{
|
2018-06-27 15:43:54 +00:00
|
|
|
struct Layer {
|
|
|
|
Raster first;
|
|
|
|
std::stringstream second;
|
|
|
|
|
|
|
|
Layer() {}
|
|
|
|
|
|
|
|
Layer(const Layer&) = delete;
|
|
|
|
Layer(Layer&& m):
|
|
|
|
first(std::move(m.first))/*, second(std::move(m.second))*/ {}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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.
|
2018-09-19 12:54:37 +00:00
|
|
|
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;
|
2018-11-13 16:33:03 +00:00
|
|
|
double m_layer_height = .0;
|
2018-12-13 11:42:45 +00:00
|
|
|
Raster::Origin m_o = Raster::Origin::TOP_LEFT;
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
std::string createIniContent(const std::string& projectname) {
|
2018-11-13 16:33:03 +00:00
|
|
|
double layer_height = m_layer_height;
|
2018-06-27 15:43:54 +00:00
|
|
|
|
2018-06-28 11:21:24 +00:00
|
|
|
using std::string;
|
|
|
|
using std::to_string;
|
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
auto expt_str = to_string(m_exp_time_s);
|
|
|
|
auto expt_first_str = to_string(m_exp_time_first_s);
|
2018-06-28 11:21:24 +00:00
|
|
|
auto stepnum_str = to_string(static_cast<unsigned>(800*layer_height));
|
|
|
|
auto layerh_str = to_string(layer_height);
|
|
|
|
|
|
|
|
return string(
|
2018-06-27 15:43:54 +00:00
|
|
|
"action = print\n"
|
|
|
|
"jobDir = ") + projectname + "\n" +
|
2018-06-28 11:21:24 +00:00
|
|
|
"expTime = " + expt_str + "\n"
|
|
|
|
"expTimeFirst = " + expt_first_str + "\n"
|
|
|
|
"stepNum = " + stepnum_str + "\n"
|
2018-06-27 15:43:54 +00:00
|
|
|
"wifiOn = 1\n"
|
|
|
|
"tiltSlow = 60\n"
|
|
|
|
"tiltFast = 15\n"
|
|
|
|
"numFade = 10\n"
|
|
|
|
"startdelay = 0\n"
|
2018-06-28 11:21:24 +00:00
|
|
|
"layerHeight = " + layerh_str + "\n"
|
2018-06-27 15:43:54 +00:00
|
|
|
"noteInfo = "
|
2018-07-03 15:05:09 +00:00
|
|
|
"expTime="+expt_str+"+resinType=generic+layerHeight="
|
2018-06-28 11:21:24 +00:00
|
|
|
+layerh_str+"+printer=DWARF3\n";
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
2018-12-13 11:42:45 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2018-06-27 15:43:54 +00:00
|
|
|
inline FilePrinter(double width_mm, double height_mm,
|
|
|
|
unsigned width_px, unsigned height_px,
|
2018-11-13 16:33:03 +00:00
|
|
|
double layer_height,
|
2018-12-13 11:42:45 +00:00
|
|
|
double exp_time, double exp_time_first,
|
|
|
|
RasterOrientation ro = RO_PORTRAIT):
|
2018-09-19 12:54:37 +00:00
|
|
|
m_res(width_px, height_px),
|
|
|
|
m_pxdim(width_mm/width_px, height_mm/height_px),
|
|
|
|
m_exp_time_s(exp_time),
|
2018-11-13 16:33:03 +00:00
|
|
|
m_exp_time_first_s(exp_time_first),
|
2018-12-13 11:42:45 +00:00
|
|
|
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 )
|
2018-06-28 11:21:24 +00:00
|
|
|
{
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FilePrinter(const FilePrinter& ) = delete;
|
|
|
|
FilePrinter(FilePrinter&& m):
|
2018-09-19 12:54:37 +00:00
|
|
|
m_layers_rst(std::move(m.m_layers_rst)),
|
|
|
|
m_res(m.m_res),
|
|
|
|
m_pxdim(m.m_pxdim) {}
|
2018-06-27 15:43:54 +00:00
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
|
|
|
|
inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
|
2018-06-27 15:43:54 +00:00
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
inline void draw_polygon(const ExPolygon& p, unsigned lyr) {
|
|
|
|
assert(lyr < m_layers_rst.size());
|
|
|
|
m_layers_rst[lyr].first.draw(p);
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
inline void begin_layer(unsigned lyr) {
|
|
|
|
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
|
2018-12-13 11:42:45 +00:00
|
|
|
m_layers_rst[lyr].first.reset(m_res, m_pxdim, m_o);
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
inline void begin_layer() {
|
|
|
|
m_layers_rst.emplace_back();
|
2018-12-13 11:42:45 +00:00
|
|
|
m_layers_rst.front().first.reset(m_res, m_pxdim, m_o);
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
inline void finish_layer(unsigned lyr_id) {
|
|
|
|
assert(lyr_id < m_layers_rst.size());
|
|
|
|
m_layers_rst[lyr_id].first.save(m_layers_rst[lyr_id].second,
|
2018-06-27 15:43:54 +00:00
|
|
|
Raster::Compression::PNG);
|
2018-09-19 12:54:37 +00:00
|
|
|
m_layers_rst[lyr_id].first.reset();
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
inline void finish_layer() {
|
|
|
|
if(!m_layers_rst.empty()) {
|
|
|
|
m_layers_rst.back().first.save(m_layers_rst.back().second,
|
2018-06-27 15:43:54 +00:00
|
|
|
Raster::Compression::PNG);
|
2018-09-19 12:54:37 +00:00
|
|
|
m_layers_rst.back().first.reset();
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-13 16:33:03 +00:00
|
|
|
template<class LyrFmt>
|
2018-06-27 15:43:54 +00:00
|
|
|
inline void save(const std::string& path) {
|
2018-09-19 09:08:10 +00:00
|
|
|
try {
|
2018-11-13 16:33:03 +00:00
|
|
|
LayerWriter<LyrFmt> writer(path);
|
2018-12-13 17:49:08 +00:00
|
|
|
if(!writer.is_ok()) return;
|
2018-09-19 09:08:10 +00:00
|
|
|
|
2018-09-19 11:43:15 +00:00
|
|
|
std::string project = writer.get_name();
|
2018-09-19 09:08:10 +00:00
|
|
|
|
2018-09-19 11:43:15 +00:00
|
|
|
writer.next_entry("config.ini");
|
2018-12-13 17:49:08 +00:00
|
|
|
if(!writer.is_ok()) return;
|
|
|
|
|
2018-09-19 11:43:15 +00:00
|
|
|
writer << createIniContent(project);
|
2018-09-19 09:08:10 +00:00
|
|
|
|
2018-12-13 17:49:08 +00:00
|
|
|
for(unsigned i = 0; i < m_layers_rst.size() && writer.is_ok(); i++)
|
|
|
|
{
|
2018-09-19 12:54:37 +00:00
|
|
|
if(m_layers_rst[i].second.rdbuf()->in_avail() > 0) {
|
2018-09-19 09:08:10 +00:00
|
|
|
char lyrnum[6];
|
|
|
|
std::sprintf(lyrnum, "%.5d", i);
|
|
|
|
auto zfilename = project + lyrnum + ".png";
|
2018-09-19 11:43:15 +00:00
|
|
|
writer.next_entry(zfilename);
|
2018-12-13 17:49:08 +00:00
|
|
|
|
|
|
|
if(!writer.is_ok()) break;
|
|
|
|
|
2018-11-29 17:12:40 +00:00
|
|
|
writer << m_layers_rst[i].second.str();
|
|
|
|
// writer << m_layers_rst[i].second.rdbuf();
|
|
|
|
// we can keep the date for later calls of this method
|
|
|
|
//m_layers_rst[i].second.str("");
|
2018-09-19 09:08:10 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-19 11:43:15 +00:00
|
|
|
} catch(std::exception& e) {
|
|
|
|
BOOST_LOG_TRIVIAL(error) << e.what();
|
2018-12-13 17:49:08 +00:00
|
|
|
// Rethrow the exception
|
|
|
|
throw;
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-19 12:54:37 +00:00
|
|
|
void save_layer(unsigned lyr, const std::string& path) {
|
2018-06-27 15:43:54 +00:00
|
|
|
unsigned i = lyr;
|
2018-09-19 12:54:37 +00:00
|
|
|
assert(i < m_layers_rst.size());
|
2018-06-27 15:43:54 +00:00
|
|
|
|
|
|
|
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()) {
|
2018-09-19 12:54:37 +00:00
|
|
|
m_layers_rst[i].first.save(out, Raster::Compression::PNG);
|
2018-06-27 15:43:54 +00:00
|
|
|
} else {
|
|
|
|
BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
|
|
|
|
}
|
|
|
|
|
|
|
|
out.close();
|
2018-09-19 12:54:37 +00:00
|
|
|
m_layers_rst[i].first.reset();
|
2018-06-27 15:43:54 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // PRINTEXPORT_HPP
|