Merge branch 'tm_sla_printer_mirror'
This commit is contained in:
commit
967dba2b90
14 changed files with 486 additions and 478 deletions
|
@ -130,13 +130,10 @@ add_library(libslic3r STATIC
|
||||||
Print.hpp
|
Print.hpp
|
||||||
PrintBase.cpp
|
PrintBase.cpp
|
||||||
PrintBase.hpp
|
PrintBase.hpp
|
||||||
PrintExport.hpp
|
|
||||||
PrintConfig.cpp
|
PrintConfig.cpp
|
||||||
PrintConfig.hpp
|
PrintConfig.hpp
|
||||||
PrintObject.cpp
|
PrintObject.cpp
|
||||||
PrintRegion.cpp
|
PrintRegion.cpp
|
||||||
Rasterizer/Rasterizer.hpp
|
|
||||||
Rasterizer/Rasterizer.cpp
|
|
||||||
SLAPrint.cpp
|
SLAPrint.cpp
|
||||||
SLAPrint.hpp
|
SLAPrint.hpp
|
||||||
SLA/SLAAutoSupports.hpp
|
SLA/SLAAutoSupports.hpp
|
||||||
|
@ -175,6 +172,10 @@ add_library(libslic3r STATIC
|
||||||
SLA/SLARotfinder.cpp
|
SLA/SLARotfinder.cpp
|
||||||
SLA/SLABoostAdapter.hpp
|
SLA/SLABoostAdapter.hpp
|
||||||
SLA/SLASpatIndex.hpp
|
SLA/SLASpatIndex.hpp
|
||||||
|
SLA/SLARaster.hpp
|
||||||
|
SLA/SLARaster.cpp
|
||||||
|
SLA/SLARasterWriter.hpp
|
||||||
|
SLA/SLARasterWriter.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||||
|
|
|
@ -2258,6 +2258,20 @@ void PrintConfigDef::init_sla_params()
|
||||||
def->min = 100;
|
def->min = 100;
|
||||||
def->set_default_value(new ConfigOptionInt(1440));
|
def->set_default_value(new ConfigOptionInt(1440));
|
||||||
|
|
||||||
|
def = this->add("display_mirror_x", coBool);
|
||||||
|
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 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);
|
def = this->add("display_orientation", coEnum);
|
||||||
def->label = L("Display orientation");
|
def->label = L("Display orientation");
|
||||||
def->tooltip = L("Set the actual LCD display orientation inside the SLA printer."
|
def->tooltip = L("Set the actual LCD display orientation inside the SLA printer."
|
||||||
|
|
|
@ -1083,6 +1083,8 @@ public:
|
||||||
ConfigOptionInt display_pixels_x;
|
ConfigOptionInt display_pixels_x;
|
||||||
ConfigOptionInt display_pixels_y;
|
ConfigOptionInt display_pixels_y;
|
||||||
ConfigOptionEnum<SLADisplayOrientation> display_orientation;
|
ConfigOptionEnum<SLADisplayOrientation> display_orientation;
|
||||||
|
ConfigOptionBool display_mirror_x;
|
||||||
|
ConfigOptionBool display_mirror_y;
|
||||||
ConfigOptionFloats relative_correction;
|
ConfigOptionFloats relative_correction;
|
||||||
ConfigOptionFloat absolute_correction;
|
ConfigOptionFloat absolute_correction;
|
||||||
ConfigOptionFloat gamma_correction;
|
ConfigOptionFloat gamma_correction;
|
||||||
|
@ -1099,6 +1101,8 @@ protected:
|
||||||
OPT_PTR(display_height);
|
OPT_PTR(display_height);
|
||||||
OPT_PTR(display_pixels_x);
|
OPT_PTR(display_pixels_x);
|
||||||
OPT_PTR(display_pixels_y);
|
OPT_PTR(display_pixels_y);
|
||||||
|
OPT_PTR(display_mirror_x);
|
||||||
|
OPT_PTR(display_mirror_y);
|
||||||
OPT_PTR(display_orientation);
|
OPT_PTR(display_orientation);
|
||||||
OPT_PTR(relative_correction);
|
OPT_PTR(relative_correction);
|
||||||
OPT_PTR(absolute_correction);
|
OPT_PTR(absolute_correction);
|
||||||
|
|
|
@ -1,327 +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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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,5 +1,10 @@
|
||||||
#include "Rasterizer.hpp"
|
#ifndef SLARASTER_CPP
|
||||||
#include <ExPolygon.hpp>
|
#define SLARASTER_CPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "SLARaster.hpp"
|
||||||
|
#include "libslic3r/ExPolygon.hpp"
|
||||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
||||||
|
|
||||||
// For rasterizing
|
// For rasterizing
|
||||||
|
@ -19,11 +24,13 @@
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
const Polygon& contour(const ExPolygon& p) { return p.contour; }
|
inline const Polygon& contour(const ExPolygon& p) { return p.contour; }
|
||||||
const ClipperLib::Path& contour(const ClipperLib::Polygon& 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; }
|
inline const Polygons& holes(const ExPolygon& p) { return p.holes; }
|
||||||
const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
|
inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
|
||||||
|
|
||||||
|
namespace sla {
|
||||||
|
|
||||||
class Raster::Impl {
|
class Raster::Impl {
|
||||||
public:
|
public:
|
||||||
|
@ -39,7 +46,7 @@ public:
|
||||||
static const TPixel ColorWhite;
|
static const TPixel ColorWhite;
|
||||||
static const TPixel ColorBlack;
|
static const TPixel ColorBlack;
|
||||||
|
|
||||||
using Origin = Raster::Origin;
|
using Format = Raster::Format;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Raster::Resolution m_resolution;
|
Raster::Resolution m_resolution;
|
||||||
|
@ -52,16 +59,21 @@ private:
|
||||||
TRendererAA m_renderer;
|
TRendererAA m_renderer;
|
||||||
|
|
||||||
std::function<double(double)> m_gammafn;
|
std::function<double(double)> m_gammafn;
|
||||||
Origin m_o;
|
std::array<bool, 2> m_mirror;
|
||||||
|
Format m_fmt = Format::PNG;
|
||||||
|
|
||||||
inline void flipy(agg::path_storage& path) const {
|
inline void flipy(agg::path_storage& path) const {
|
||||||
path.flip_y(0, m_resolution.height_px);
|
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:
|
public:
|
||||||
|
|
||||||
inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
|
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_resolution(res),
|
||||||
// m_pxdim(pd),
|
// m_pxdim(pd),
|
||||||
m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm),
|
m_pxdim_scaled(SCALING_FACTOR / pd.w_mm, SCALING_FACTOR / pd.h_mm),
|
||||||
|
@ -72,7 +84,7 @@ public:
|
||||||
m_pixfmt(m_rbuf),
|
m_pixfmt(m_rbuf),
|
||||||
m_raw_renderer(m_pixfmt),
|
m_raw_renderer(m_pixfmt),
|
||||||
m_renderer(m_raw_renderer),
|
m_renderer(m_raw_renderer),
|
||||||
m_o(o)
|
m_mirror(mirror)
|
||||||
{
|
{
|
||||||
m_renderer.color(ColorWhite);
|
m_renderer.color(ColorWhite);
|
||||||
|
|
||||||
|
@ -81,6 +93,19 @@ public:
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Impl(const Raster::Resolution& res,
|
||||||
|
const Raster::PixelDim &pd,
|
||||||
|
Format fmt,
|
||||||
|
double gamma = 1.0):
|
||||||
|
Impl(res, pd, {false, false}, gamma)
|
||||||
|
{
|
||||||
|
switch (fmt) {
|
||||||
|
case Format::PNG: m_mirror = {false, true}; break;
|
||||||
|
case Format::RAW: m_mirror = {false, false}; break;
|
||||||
|
}
|
||||||
|
m_fmt = fmt;
|
||||||
|
}
|
||||||
|
|
||||||
template<class P> void draw(const P &poly) {
|
template<class P> void draw(const P &poly) {
|
||||||
agg::rasterizer_scanline_aa<> ras;
|
agg::rasterizer_scanline_aa<> ras;
|
||||||
|
@ -89,14 +114,16 @@ public:
|
||||||
ras.gamma(m_gammafn);
|
ras.gamma(m_gammafn);
|
||||||
|
|
||||||
auto&& path = to_path(contour(poly));
|
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);
|
ras.add_path(path);
|
||||||
|
|
||||||
for(auto& h : holes(poly)) {
|
for(auto& h : holes(poly)) {
|
||||||
auto&& holepath = to_path(h);
|
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);
|
ras.add_path(holepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,11 +135,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline TBuffer& buffer() { return m_buf; }
|
inline TBuffer& buffer() { return m_buf; }
|
||||||
|
|
||||||
|
inline Format format() const { return m_fmt; }
|
||||||
|
|
||||||
inline const Raster::Resolution resolution() { return m_resolution; }
|
inline const Raster::Resolution resolution() { return m_resolution; }
|
||||||
|
|
||||||
inline Origin origin() const /*noexcept*/ { return m_o; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
inline double getPx(const Point& p) {
|
inline double getPx(const Point& p) {
|
||||||
return p(0) * m_pxdim_scaled.w_mm;
|
return p(0) * m_pxdim_scaled.w_mm;
|
||||||
|
@ -154,30 +181,30 @@ private:
|
||||||
const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255);
|
const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255);
|
||||||
const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0);
|
const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0);
|
||||||
|
|
||||||
Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o, double g):
|
template<> Raster::Raster() { reset(); };
|
||||||
m_impl(new Impl(r, pd, o, g)) {}
|
Raster::~Raster() = default;
|
||||||
|
|
||||||
Raster::Raster() {}
|
// Raster::Raster(Raster &&m) = default;
|
||||||
|
// Raster& Raster::operator=(Raster&&) = default;
|
||||||
|
|
||||||
Raster::~Raster() {}
|
// FIXME: remove after migrating to higher version of windows compiler
|
||||||
|
Raster::Raster(Raster &&m): m_impl(std::move(m.m_impl)) {}
|
||||||
Raster::Raster(Raster &&m):
|
Raster& Raster::operator=(Raster &&m) {
|
||||||
m_impl(std::move(m.m_impl)) {}
|
m_impl = std::move(m.m_impl); return *this;
|
||||||
|
|
||||||
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
|
|
||||||
double g)
|
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
|
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
|
||||||
Raster::Origin o, double gamma)
|
Format fmt, double gamma)
|
||||||
{
|
{
|
||||||
m_impl.reset();
|
m_impl.reset();
|
||||||
m_impl.reset(new Impl(r, pd, o, gamma));
|
m_impl.reset(new Impl(r, pd, fmt, gamma));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
|
||||||
|
const std::array<bool, 2>& mirror, double gamma)
|
||||||
|
{
|
||||||
|
m_impl.reset();
|
||||||
|
m_impl.reset(new Impl(r, pd, mirror, gamma));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Raster::reset()
|
void Raster::reset()
|
||||||
|
@ -208,13 +235,13 @@ void Raster::draw(const ClipperLib::Polygon &poly)
|
||||||
m_impl->draw(poly);
|
m_impl->draw(poly);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Raster::save(std::ostream& stream, Compression comp)
|
void Raster::save(std::ostream& stream, Format fmt)
|
||||||
{
|
{
|
||||||
assert(m_impl);
|
assert(m_impl);
|
||||||
if(!stream.good()) return;
|
if(!stream.good()) return;
|
||||||
|
|
||||||
switch(comp) {
|
switch(fmt) {
|
||||||
case Compression::PNG: {
|
case Format::PNG: {
|
||||||
auto& b = m_impl->buffer();
|
auto& b = m_impl->buffer();
|
||||||
size_t out_len = 0;
|
size_t out_len = 0;
|
||||||
void * rawdata = tdefl_write_image_to_png_file_in_memory(
|
void * rawdata = tdefl_write_image_to_png_file_in_memory(
|
||||||
|
@ -231,7 +258,7 @@ void Raster::save(std::ostream& stream, Compression comp)
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Compression::RAW: {
|
case Format::RAW: {
|
||||||
stream << "P5 "
|
stream << "P5 "
|
||||||
<< m_impl->resolution().width_px << " "
|
<< m_impl->resolution().width_px << " "
|
||||||
<< m_impl->resolution().height_px << " "
|
<< m_impl->resolution().height_px << " "
|
||||||
|
@ -244,14 +271,19 @@ void Raster::save(std::ostream& stream, Compression comp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RawBytes Raster::save(Raster::Compression comp)
|
void Raster::save(std::ostream &stream)
|
||||||
|
{
|
||||||
|
save(stream, m_impl->format());
|
||||||
|
}
|
||||||
|
|
||||||
|
RawBytes Raster::save(Format fmt)
|
||||||
{
|
{
|
||||||
assert(m_impl);
|
assert(m_impl);
|
||||||
|
|
||||||
std::vector<std::uint8_t> data; size_t s = 0;
|
std::vector<std::uint8_t> data; size_t s = 0;
|
||||||
|
|
||||||
switch(comp) {
|
switch(fmt) {
|
||||||
case Compression::PNG: {
|
case Format::PNG: {
|
||||||
void *rawdata = tdefl_write_image_to_png_file_in_memory(
|
void *rawdata = tdefl_write_image_to_png_file_in_memory(
|
||||||
m_impl->buffer().data(),
|
m_impl->buffer().data(),
|
||||||
int(resolution().width_px),
|
int(resolution().width_px),
|
||||||
|
@ -265,7 +297,7 @@ RawBytes Raster::save(Raster::Compression comp)
|
||||||
MZ_FREE(rawdata);
|
MZ_FREE(rawdata);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Compression::RAW: {
|
case Format::RAW: {
|
||||||
auto header = std::string("P5 ") +
|
auto header = std::string("P5 ") +
|
||||||
std::to_string(m_impl->resolution().width_px) + " " +
|
std::to_string(m_impl->resolution().width_px) + " " +
|
||||||
std::to_string(m_impl->resolution().height_px) + " " + "255 ";
|
std::to_string(m_impl->resolution().height_px) + " " + "255 ";
|
||||||
|
@ -286,4 +318,12 @@ RawBytes Raster::save(Raster::Compression comp)
|
||||||
return {std::move(data)};
|
return {std::move(data)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RawBytes Raster::save()
|
||||||
|
{
|
||||||
|
return save(m_impl->format());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SLARASTER_CPP
|
|
@ -1,17 +1,21 @@
|
||||||
#ifndef RASTERIZER_HPP
|
#ifndef SLARASTER_HPP
|
||||||
#define RASTERIZER_HPP
|
#define SLARASTER_HPP
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <utility>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace ClipperLib { struct Polygon; }
|
namespace ClipperLib { struct Polygon; }
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class ExPolygon;
|
class ExPolygon;
|
||||||
|
|
||||||
|
namespace sla {
|
||||||
|
|
||||||
// Raw byte buffer paired with its size. Suitable for compressed PNG data.
|
// Raw byte buffer paired with its size. Suitable for compressed PNG data.
|
||||||
class RawBytes {
|
class RawBytes {
|
||||||
|
|
||||||
|
@ -23,15 +27,18 @@ public:
|
||||||
|
|
||||||
size_t size() const { return m_buffer.size(); }
|
size_t size() const { return m_buffer.size(); }
|
||||||
const uint8_t * data() { return m_buffer.data(); }
|
const uint8_t * data() { return m_buffer.data(); }
|
||||||
|
|
||||||
|
RawBytes(const RawBytes&) = delete;
|
||||||
|
RawBytes& operator=(const RawBytes&) = delete;
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
// FIXME: the following is needed for MSVC2013 compatibility
|
// FIXME: the following is needed for MSVC2013 compatibility
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
RawBytes(const RawBytes&) = delete;
|
// RawBytes(RawBytes&&) = default;
|
||||||
RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {}
|
// RawBytes& operator=(RawBytes&&) = default;
|
||||||
|
|
||||||
RawBytes& operator=(const RawBytes&) = delete;
|
RawBytes(RawBytes&& mv) : m_buffer(std::move(mv.m_buffer)) {}
|
||||||
RawBytes& operator=(RawBytes&& mv) {
|
RawBytes& operator=(RawBytes&& mv) {
|
||||||
m_buffer = std::move(mv.m_buffer);
|
m_buffer = std::move(mv.m_buffer);
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -54,28 +61,19 @@ class Raster {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Supported compression types
|
/// Supported compression types
|
||||||
enum class Compression {
|
enum class Format {
|
||||||
RAW, //!> Uncompressed pixel data
|
RAW, //!> Uncompressed pixel data
|
||||||
PNG //!> PNG compression
|
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.
|
/// Type that represents a resolution in pixels.
|
||||||
struct Resolution {
|
struct Resolution {
|
||||||
unsigned width_px;
|
unsigned width_px;
|
||||||
unsigned height_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*/ {
|
inline unsigned pixels() const /*noexcept*/ {
|
||||||
return width_px * height_px;
|
return width_px * height_px;
|
||||||
}
|
}
|
||||||
|
@ -85,24 +83,34 @@ public:
|
||||||
struct PixelDim {
|
struct PixelDim {
|
||||||
double w_mm;
|
double w_mm;
|
||||||
double h_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) {}
|
w_mm(px_width_mm), h_mm(px_height_mm) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Constructor taking the resolution and the pixel dimension.
|
/// Constructor taking the resolution and the pixel dimension.
|
||||||
Raster(const Resolution& r, const PixelDim& pd,
|
template <class...Args> Raster(Args...args) {
|
||||||
Origin o = Origin::BOTTOM_LEFT, double gamma = 1.0);
|
reset(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
Raster();
|
|
||||||
Raster(const Raster& cpy) = delete;
|
Raster(const Raster& cpy) = delete;
|
||||||
Raster& operator=(const Raster& cpy) = delete;
|
Raster& operator=(const Raster& cpy) = delete;
|
||||||
Raster(Raster&& m);
|
Raster(Raster&& m);
|
||||||
|
Raster& operator=(Raster&&);
|
||||||
~Raster();
|
~Raster();
|
||||||
|
|
||||||
/// Reallocated everything for the given resolution and pixel dimension.
|
/// Reallocated everything for the given resolution and pixel dimension.
|
||||||
void reset(const Resolution& r, const PixelDim& pd, double gamma = 1.0);
|
/// The third parameter is either the X, Y mirroring or a supported format
|
||||||
void reset(const Resolution& r, const PixelDim& pd, Origin o, double gamma);
|
/// for which the correct mirroring will be configured.
|
||||||
|
void reset(const Resolution&,
|
||||||
|
const PixelDim&,
|
||||||
|
const std::array<bool, 2>& mirror,
|
||||||
|
double gamma = 1.0);
|
||||||
|
|
||||||
|
void reset(const Resolution& r,
|
||||||
|
const PixelDim& pd,
|
||||||
|
Format o,
|
||||||
|
double gamma = 1.0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Release the allocated resources. Drawing in this state ends in
|
* Release the allocated resources. Drawing in this state ends in
|
||||||
* unspecified behavior.
|
* unspecified behavior.
|
||||||
|
@ -119,11 +127,24 @@ public:
|
||||||
void draw(const ExPolygon& poly);
|
void draw(const ExPolygon& poly);
|
||||||
void draw(const ClipperLib::Polygon& 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.
|
/// Save the raster on the specified stream.
|
||||||
void save(std::ostream& stream, Compression comp = Compression::RAW);
|
void save(std::ostream& stream, Format);
|
||||||
|
void save(std::ostream& stream);
|
||||||
|
|
||||||
RawBytes save(Compression comp = Compression::RAW);
|
/// Save into a continuous byte stream which is returned.
|
||||||
|
RawBytes save(Format fmt);
|
||||||
|
RawBytes save();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
// This prevents the duplicate default constructor warning on MSVC2013
|
||||||
#endif // RASTERIZER_HPP
|
template<> Raster::Raster();
|
||||||
|
|
||||||
|
|
||||||
|
} // 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 = Point(p.y(), p.x());
|
||||||
|
std::reverse(poly.contour.points.begin(), poly.contour.points.end());
|
||||||
|
|
||||||
|
for(auto& h : poly.holes) {
|
||||||
|
for(auto& p : h.points) p = Point(p.y(), p.x());
|
||||||
|
std::reverse(h.points.begin(), h.points.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SLARasterWriter::SLARasterWriter(const SLAPrinterConfig &cfg,
|
||||||
|
const SLAMaterialConfig &mcfg,
|
||||||
|
double layer_height)
|
||||||
|
{
|
||||||
|
double w = cfg.display_width.getFloat();
|
||||||
|
double h = cfg.display_height.getFloat();
|
||||||
|
auto pw = unsigned(cfg.display_pixels_x.getInt());
|
||||||
|
auto ph = unsigned(cfg.display_pixels_y.getInt());
|
||||||
|
|
||||||
|
m_mirror[X] = cfg.display_mirror_x.getBool();
|
||||||
|
|
||||||
|
// PNG raster will implicitly do an Y mirror
|
||||||
|
m_mirror[Y] = ! cfg.display_mirror_y.getBool();
|
||||||
|
|
||||||
|
auto ro = cfg.display_orientation.getInt();
|
||||||
|
|
||||||
|
if(ro == roPortrait) {
|
||||||
|
std::swap(w, h);
|
||||||
|
std::swap(pw, ph);
|
||||||
|
m_o = roPortrait;
|
||||||
|
|
||||||
|
// XY flipping implicitly does an X mirror
|
||||||
|
m_mirror[X] = ! m_mirror[X];
|
||||||
|
} else m_o = roLandscape;
|
||||||
|
|
||||||
|
m_res = Raster::Resolution(pw, ph);
|
||||||
|
m_pxdim = Raster::PixelDim(w/pw, h/ph);
|
||||||
|
m_exp_time_s = mcfg.exposure_time.getFloat();
|
||||||
|
m_exp_time_first_s = mcfg.initial_exposure_time.getFloat();
|
||||||
|
m_layer_height = layer_height;
|
||||||
|
|
||||||
|
m_gamma = cfg.gamma_correction.getFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SLARasterWriter::save(const std::string &fpath, const std::string &prjname)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Zipper zipper(fpath); // zipper with no compression
|
||||||
|
|
||||||
|
std::string project = prjname.empty()?
|
||||||
|
boost::filesystem::path(fpath).stem().string() : prjname;
|
||||||
|
|
||||||
|
zipper.add_entry("config.ini");
|
||||||
|
|
||||||
|
zipper << createIniContent(project);
|
||||||
|
|
||||||
|
for(unsigned i = 0; i < m_layers_rst.size(); i++)
|
||||||
|
{
|
||||||
|
if(m_layers_rst[i].rawbytes.size() > 0) {
|
||||||
|
char lyrnum[6];
|
||||||
|
std::sprintf(lyrnum, "%.5d", i);
|
||||||
|
auto zfilename = project + lyrnum + ".png";
|
||||||
|
|
||||||
|
// Add binary entry to the zipper
|
||||||
|
zipper.add_entry(zfilename,
|
||||||
|
m_layers_rst[i].rawbytes.data(),
|
||||||
|
m_layers_rst[i].rawbytes.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zipper.finalize();
|
||||||
|
} catch(std::exception& e) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||||
|
// Rethrow the exception
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SLARasterWriter::set_statistics(const std::vector<double> statistics)
|
||||||
|
{
|
||||||
|
if (statistics.size() != psCnt)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_used_material = statistics[psUsedMaterial];
|
||||||
|
m_cnt_fade_layers = int(statistics[psNumFade]);
|
||||||
|
m_cnt_slow_layers = int(statistics[psNumSlow]);
|
||||||
|
m_cnt_fast_layers = int(statistics[psNumFast]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sla
|
||||||
|
} // namespace Slic3r
|
167
src/libslic3r/SLA/SLARasterWriter.hpp
Normal file
167
src/libslic3r/SLA/SLARasterWriter.hpp
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
#ifndef SLARASTERWRITER_HPP
|
||||||
|
#define SLARASTERWRITER_HPP
|
||||||
|
|
||||||
|
// For png export of the sliced model
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
|
||||||
|
#include "SLARaster.hpp"
|
||||||
|
|
||||||
|
namespace Slic3r { namespace sla {
|
||||||
|
|
||||||
|
// Implementation for PNG raster output
|
||||||
|
// Be aware that if a large number of layers are allocated, it can very well
|
||||||
|
// exhaust the available memory especially on 32 bit platform.
|
||||||
|
// This class is designed to be used in parallel mode. Layers have an ID and
|
||||||
|
// each layer can be written and compressed independently (in parallel).
|
||||||
|
// At the end when all layers where written, the save method can be used to
|
||||||
|
// write out the result into a zipped archive.
|
||||||
|
class SLARasterWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum RasterOrientation {
|
||||||
|
roLandscape,
|
||||||
|
roPortrait
|
||||||
|
};
|
||||||
|
|
||||||
|
// Used for addressing parameters of set_statistics()
|
||||||
|
enum ePrintStatistics
|
||||||
|
{
|
||||||
|
psUsedMaterial = 0,
|
||||||
|
psNumFade,
|
||||||
|
psNumSlow,
|
||||||
|
psNumFast,
|
||||||
|
|
||||||
|
psCnt
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// A struct to bind the raster image data and its compressed bytes together.
|
||||||
|
struct Layer {
|
||||||
|
Raster raster;
|
||||||
|
RawBytes rawbytes;
|
||||||
|
|
||||||
|
Layer() = default;
|
||||||
|
Layer(const Layer&) = delete; // The image is big, do not copy by accident
|
||||||
|
Layer& operator=(const Layer&) = delete;
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////
|
||||||
|
// FIXME: the following is needed for MSVC2013 compatibility
|
||||||
|
// /////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Layer(Layer&& m) = default;
|
||||||
|
// Layer& operator=(Layer&&) = default;
|
||||||
|
Layer(Layer &&m):
|
||||||
|
raster(std::move(m.raster)), rawbytes(std::move(m.rawbytes)) {}
|
||||||
|
Layer& operator=(Layer &&m) {
|
||||||
|
raster = std::move(m.raster); rawbytes = std::move(m.rawbytes);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// We will save the compressed PNG data into RawBytes type buffers in
|
||||||
|
// parallel. Later we can write every layer to the disk sequentially.
|
||||||
|
std::vector<Layer> m_layers_rst;
|
||||||
|
Raster::Resolution m_res;
|
||||||
|
Raster::PixelDim m_pxdim;
|
||||||
|
double m_exp_time_s = .0, m_exp_time_first_s = .0;
|
||||||
|
double m_layer_height = .0;
|
||||||
|
RasterOrientation m_o = roPortrait;
|
||||||
|
std::array<bool, 2> m_mirror;
|
||||||
|
|
||||||
|
double m_gamma;
|
||||||
|
|
||||||
|
double m_used_material = 0.0;
|
||||||
|
int m_cnt_fade_layers = 0;
|
||||||
|
int m_cnt_slow_layers = 0;
|
||||||
|
int m_cnt_fast_layers = 0;
|
||||||
|
|
||||||
|
std::string createIniContent(const std::string& projectname) const;
|
||||||
|
|
||||||
|
static void flpXY(ClipperLib::Polygon& poly);
|
||||||
|
static void flpXY(ExPolygon& poly);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SLARasterWriter(const SLAPrinterConfig& cfg,
|
||||||
|
const SLAMaterialConfig& mcfg,
|
||||||
|
double layer_height);
|
||||||
|
|
||||||
|
SLARasterWriter(const SLARasterWriter& ) = delete;
|
||||||
|
SLARasterWriter& operator=(const SLARasterWriter&) = delete;
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
// FIXME: the following is needed for MSVC2013 compatibility
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// SLARasterWriter(SLARasterWriter&& m) = default;
|
||||||
|
// SLARasterWriter& operator=(SLARasterWriter&&) = default;
|
||||||
|
SLARasterWriter(SLARasterWriter&& m):
|
||||||
|
m_layers_rst(std::move(m.m_layers_rst)),
|
||||||
|
m_res(m.m_res),
|
||||||
|
m_pxdim(m.m_pxdim),
|
||||||
|
m_exp_time_s(m.m_exp_time_s),
|
||||||
|
m_exp_time_first_s(m.m_exp_time_first_s),
|
||||||
|
m_layer_height(m.m_layer_height),
|
||||||
|
m_o(m.m_o),
|
||||||
|
m_mirror(std::move(m.m_mirror)),
|
||||||
|
m_gamma(m.m_gamma),
|
||||||
|
m_used_material(m.m_used_material),
|
||||||
|
m_cnt_fade_layers(m.m_cnt_fade_layers),
|
||||||
|
m_cnt_slow_layers(m.m_cnt_slow_layers),
|
||||||
|
m_cnt_fast_layers(m.m_cnt_fast_layers)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
|
||||||
|
inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
|
||||||
|
|
||||||
|
template<class Poly> void draw_polygon(const Poly& p, unsigned lyr) {
|
||||||
|
assert(lyr < m_layers_rst.size());
|
||||||
|
if(m_o == roPortrait) {
|
||||||
|
Poly poly(p); flpXY(poly);
|
||||||
|
m_layers_rst[lyr].raster.draw(poly);
|
||||||
|
}
|
||||||
|
else m_layers_rst[lyr].raster.draw(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void begin_layer(unsigned lyr) {
|
||||||
|
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
|
||||||
|
m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_mirror, m_gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void begin_layer() {
|
||||||
|
m_layers_rst.emplace_back();
|
||||||
|
m_layers_rst.front().raster.reset(m_res, m_pxdim, m_mirror, m_gamma);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void finish_layer(unsigned lyr_id) {
|
||||||
|
assert(lyr_id < m_layers_rst.size());
|
||||||
|
m_layers_rst[lyr_id].rawbytes =
|
||||||
|
m_layers_rst[lyr_id].raster.save(Raster::Format::PNG);
|
||||||
|
m_layers_rst[lyr_id].raster.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void finish_layer() {
|
||||||
|
if(!m_layers_rst.empty()) {
|
||||||
|
m_layers_rst.back().rawbytes =
|
||||||
|
m_layers_rst.back().raster.save(Raster::Format::PNG);
|
||||||
|
m_layers_rst.back().raster.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void save(const std::string& fpath, const std::string& prjname = "");
|
||||||
|
|
||||||
|
void set_statistics(const std::vector<double> statistics);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sla
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // SLARASTERWRITER_HPP
|
|
@ -747,8 +747,8 @@ void SLAPrint::process()
|
||||||
{
|
{
|
||||||
// We apply the printer correction offset here.
|
// We apply the printer correction offset here.
|
||||||
if(clpr_offs != 0)
|
if(clpr_offs != 0)
|
||||||
po.m_model_slices[id] =
|
po.m_model_slices[id] =
|
||||||
offset_ex(po.m_model_slices[id], float(clpr_offs));
|
offset_ex(po.m_model_slices[id], float(clpr_offs));
|
||||||
|
|
||||||
mit->set_model_slice_idx(po, id); ++mit;
|
mit->set_model_slice_idx(po, id); ++mit;
|
||||||
}
|
}
|
||||||
|
@ -1014,7 +1014,7 @@ void SLAPrint::process()
|
||||||
namespace sl = libnest2d::shapelike; // For algorithms
|
namespace sl = libnest2d::shapelike; // For algorithms
|
||||||
|
|
||||||
// If the raster has vertical orientation, we will flip the coordinates
|
// 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
|
// Set up custom union and diff functions for clipper polygons
|
||||||
auto polyunion = [] (const ClipperPolygons& subjects)
|
auto polyunion = [] (const ClipperPolygons& subjects)
|
||||||
|
@ -1072,9 +1072,9 @@ void SLAPrint::process()
|
||||||
|
|
||||||
// get polygons for all instances in the object
|
// get polygons for all instances in the object
|
||||||
auto get_all_polygons =
|
auto get_all_polygons =
|
||||||
[flpXY](const ExPolygons& input_polygons,
|
[](const ExPolygons& input_polygons,
|
||||||
const std::vector<SLAPrintObject::Instance>& instances,
|
const std::vector<SLAPrintObject::Instance>& instances,
|
||||||
bool is_lefthanded)
|
bool is_lefthanded)
|
||||||
{
|
{
|
||||||
ClipperPolygons polygons;
|
ClipperPolygons polygons;
|
||||||
polygons.reserve(input_polygons.size() * instances.size());
|
polygons.reserve(input_polygons.size() * instances.size());
|
||||||
|
@ -1088,7 +1088,7 @@ void SLAPrint::process()
|
||||||
|
|
||||||
// We need to reverse if flpXY OR is_lefthanded is true but
|
// We need to reverse if flpXY OR is_lefthanded is true but
|
||||||
// not if both are true which is a logical inequality (XOR)
|
// 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
|
// should be a move
|
||||||
poly.Contour.reserve(polygon.contour.size() + 1);
|
poly.Contour.reserve(polygon.contour.size() + 1);
|
||||||
|
@ -1123,10 +1123,10 @@ void SLAPrint::process()
|
||||||
sl::translate(poly, ClipperPoint{instances[i].shift(X),
|
sl::translate(poly, ClipperPoint{instances[i].shift(X),
|
||||||
instances[i].shift(Y)});
|
instances[i].shift(Y)});
|
||||||
|
|
||||||
if (flpXY) {
|
// if (flpXY) {
|
||||||
for(auto& p : poly.Contour) std::swap(p.X, p.Y);
|
// 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);
|
// for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
|
||||||
}
|
// }
|
||||||
|
|
||||||
polygons.emplace_back(std::move(poly));
|
polygons.emplace_back(std::move(poly));
|
||||||
}
|
}
|
||||||
|
@ -1295,35 +1295,11 @@ void SLAPrint::process()
|
||||||
auto rasterize = [this]() {
|
auto rasterize = [this]() {
|
||||||
if(canceled()) return;
|
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
|
{ // create a raster printer for the current print parameters
|
||||||
// I don't know any better
|
double layerh = m_default_object_config.layer_height.getFloat();
|
||||||
auto& ocfg = m_objects.front()->m_config;
|
m_printer.reset(new SLAPrinter(m_printer_config,
|
||||||
auto& matcfg = m_material_config;
|
m_material_config,
|
||||||
auto& printcfg = m_printer_config;
|
layerh));
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate space for all the layers
|
// Allocate space for all the layers
|
||||||
|
@ -1511,6 +1487,8 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
||||||
"display_height",
|
"display_height",
|
||||||
"display_pixels_x",
|
"display_pixels_x",
|
||||||
"display_pixels_y",
|
"display_pixels_y",
|
||||||
|
"display_mirror_x",
|
||||||
|
"display_mirror_y",
|
||||||
"display_orientation"
|
"display_orientation"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "PrintBase.hpp"
|
#include "PrintBase.hpp"
|
||||||
#include "PrintExport.hpp"
|
//#include "PrintExport.hpp"
|
||||||
|
#include "SLA/SLARasterWriter.hpp"
|
||||||
#include "Point.hpp"
|
#include "Point.hpp"
|
||||||
#include "MTUtils.hpp"
|
#include "MTUtils.hpp"
|
||||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
||||||
#include "Zipper.hpp"
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -326,37 +326,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.
|
* @brief This class is the high level FSM for the SLA printing process.
|
||||||
*
|
*
|
||||||
|
@ -389,11 +358,10 @@ public:
|
||||||
// Returns true if the last step was finished with success.
|
// 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); }
|
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,
|
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; }
|
const PrintObjects& objects() const { return m_objects; }
|
||||||
|
@ -454,7 +422,7 @@ public:
|
||||||
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
|
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
using SLAPrinter = sla::SLARasterWriter;
|
||||||
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
||||||
|
|
||||||
// Implement same logic as in SLAPrintObject
|
// Implement same logic as in SLAPrintObject
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <boost/filesystem/path.hpp>
|
#include <boost/filesystem/path.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include <wx/sizer.h>
|
#include <wx/sizer.h>
|
||||||
#include <wx/stattext.h>
|
#include <wx/stattext.h>
|
||||||
|
|
|
@ -509,6 +509,7 @@ const std::vector<std::string>& Preset::sla_printer_options()
|
||||||
"printer_technology",
|
"printer_technology",
|
||||||
"bed_shape", "max_print_height",
|
"bed_shape", "max_print_height",
|
||||||
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
|
||||||
|
"display_mirror_x", "display_mirror_y",
|
||||||
"display_orientation",
|
"display_orientation",
|
||||||
"fast_tilt_time", "slow_tilt_time", "area_fill",
|
"fast_tilt_time", "slow_tilt_time", "area_fill",
|
||||||
"relative_correction",
|
"relative_correction",
|
||||||
|
|
|
@ -2087,6 +2087,10 @@ void TabPrinter::build_sla()
|
||||||
line.append_option(optgroup->get_option("display_pixels_y"));
|
line.append_option(optgroup->get_option("display_pixels_y"));
|
||||||
optgroup->append_line(line);
|
optgroup->append_line(line);
|
||||||
optgroup->append_single_option_line("display_orientation");
|
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")));
|
optgroup = page->new_optgroup(_(L("Tilt")));
|
||||||
line = { _(L("Tilt time")), "" };
|
line = { _(L("Tilt time")), "" };
|
||||||
|
|
Loading…
Add table
Reference in a new issue