mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-16 03:07:55 -06:00
Refactor and extensions to png export dialog.
This commit is contained in:
parent
30e177d986
commit
ac9d81cfa0
11 changed files with 492 additions and 341 deletions
|
@ -12,19 +12,8 @@
|
|||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
// For png export of the sliced model
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <wx/stdstream.h>
|
||||
#include <wx/wfstream.h>
|
||||
#include <wx/zipstrm.h>
|
||||
|
||||
#include "Rasterizer/Rasterizer.hpp"
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
|
||||
|
||||
#include "slic3r/IProgressIndicator.hpp"
|
||||
#include "PrintExport.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -1263,291 +1252,8 @@ void Print::set_status(int percent, const std::string &message)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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<Print::FilePrinterFormat format>
|
||||
class FilePrinter {
|
||||
public:
|
||||
|
||||
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
|
||||
void drawPolygon(const ExPolygon& 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 beginLayer(unsigned layer);
|
||||
|
||||
// Allocate a new layer on top of the last and switch to it.
|
||||
void beginLayer();
|
||||
|
||||
/*
|
||||
* 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 finishLayer(unsigned layer);
|
||||
|
||||
// Finish the top layer.
|
||||
void finishLayer();
|
||||
|
||||
// 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.
|
||||
void saveLayer(unsigned lyr, const std::string& path);
|
||||
};
|
||||
|
||||
// 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<Print::FilePrinterFormat::PNG> {
|
||||
|
||||
struct Layer {
|
||||
Raster first;
|
||||
std::stringstream second;
|
||||
|
||||
Layer() {}
|
||||
Layer(const Raster::Resolution& res, const Raster::PixelDim& pd):
|
||||
first(res, pd) {}
|
||||
|
||||
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.
|
||||
std::vector<Layer> layers_rst_;
|
||||
Raster::Resolution res_;
|
||||
Raster::PixelDim pxdim_;
|
||||
|
||||
public:
|
||||
inline FilePrinter(unsigned width_px, unsigned height_px,
|
||||
double width_mm, double height_mm,
|
||||
unsigned layer_cnt = 0):
|
||||
res_(width_px, height_px),
|
||||
pxdim_(width_mm/width_px, height_mm/height_px) {
|
||||
layers(layer_cnt);
|
||||
}
|
||||
|
||||
FilePrinter(const FilePrinter& ) = delete;
|
||||
FilePrinter(FilePrinter&& m):
|
||||
layers_rst_(std::move(m.layers_rst_)),
|
||||
res_(m.res_),
|
||||
pxdim_(m.pxdim_) {}
|
||||
|
||||
inline void layers(unsigned cnt) { if(cnt > 0) layers_rst_.resize(cnt); }
|
||||
inline unsigned layers() const { return layers_rst_.size(); }
|
||||
|
||||
inline void drawPolygon(const ExPolygon& p, unsigned lyr) {
|
||||
assert(lyr < layers_rst_.size());
|
||||
layers_rst_[lyr].first.draw(p);
|
||||
}
|
||||
|
||||
inline void beginLayer(unsigned lyr) {
|
||||
if(layers_rst_.size() <= lyr) layers_rst_.resize(lyr+1);
|
||||
layers_rst_[lyr].first.reset(res_, pxdim_);
|
||||
}
|
||||
|
||||
inline void beginLayer() {
|
||||
layers_rst_.emplace_back();
|
||||
layers_rst_.front().first.reset(res_, pxdim_);
|
||||
}
|
||||
|
||||
inline void finishLayer(unsigned lyr_id) {
|
||||
assert(lyr_id < layers_rst_.size());
|
||||
layers_rst_[lyr_id].first.save(layers_rst_[lyr_id].second,
|
||||
Raster::Compression::PNG);
|
||||
layers_rst_[lyr_id].first.reset();
|
||||
}
|
||||
|
||||
inline void finishLayer() {
|
||||
if(!layers_rst_.empty()) {
|
||||
layers_rst_.back().first.save(layers_rst_.back().second,
|
||||
Raster::Compression::PNG);
|
||||
layers_rst_.back().first.reset();
|
||||
}
|
||||
}
|
||||
|
||||
inline void save(const std::string& path) {
|
||||
|
||||
wxFFileOutputStream zipfile(path);
|
||||
|
||||
if(!zipfile.IsOk()) {
|
||||
std::cout /*BOOST_LOG_TRIVIAL(error)*/ << "Can't create zip file for layers! " << path << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
wxZipOutputStream zipstream(zipfile);
|
||||
wxStdOutputStream pngstream(zipstream);
|
||||
|
||||
for(unsigned i = 0; i < layers_rst_.size(); i++) {
|
||||
if(layers_rst_[i].second.rdbuf()->in_avail() > 0) {
|
||||
char lyrnum[6];
|
||||
std::sprintf(lyrnum, "%.5d", i);
|
||||
auto zfilename = std::string("layer") + lyrnum + ".png";
|
||||
zipstream.PutNextEntry(zfilename);
|
||||
pngstream << layers_rst_[i].second.rdbuf();
|
||||
layers_rst_[i].second.str("");
|
||||
}
|
||||
}
|
||||
|
||||
zipstream.Close();
|
||||
zipfile.Close();
|
||||
}
|
||||
|
||||
void saveLayer(unsigned lyr, const std::string& path) {
|
||||
unsigned i = lyr;
|
||||
assert(i < 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()) {
|
||||
layers_rst_[i].first.save(out, Raster::Compression::PNG);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
|
||||
}
|
||||
|
||||
out.close();
|
||||
layers_rst_[i].first.reset();
|
||||
}
|
||||
};
|
||||
|
||||
template<Print::FilePrinterFormat format, class...Args>
|
||||
void Print::print_to(std::string dirpath,
|
||||
double width_mm,
|
||||
double height_mm,
|
||||
Args...args)
|
||||
{
|
||||
|
||||
std::string& dir = dirpath;
|
||||
|
||||
LayerPtrs layers;
|
||||
|
||||
// Merge the sliced layers with the support layers
|
||||
std::for_each(objects.begin(), objects.end(), [&layers](PrintObject *o){
|
||||
layers.insert(layers.end(), o->layers.begin(), o->layers.end());
|
||||
layers.insert(layers.end(), o->support_layers.begin(),
|
||||
o->support_layers.end());
|
||||
});
|
||||
|
||||
// Sort layers by z coord
|
||||
std::sort(layers.begin(), layers.end(), [](Layer *l1, Layer *l2) {
|
||||
return l1->print_z < l2->print_z;
|
||||
});
|
||||
|
||||
auto print_bb = bounding_box();
|
||||
|
||||
// If the print does not fit into the print area we should cry about it.
|
||||
if(unscale(print_bb.size().x) > width_mm ||
|
||||
unscale(print_bb.size().y) > height_mm) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Warning: Print will not fit!" << "\n"
|
||||
<< "Width needed: " << unscale(print_bb.size().x) << "\n"
|
||||
<< "Height needed: " << unscale(print_bb.size().y) << "\n";
|
||||
}
|
||||
|
||||
// Offset for centering the print onto the print area
|
||||
auto cx = scale_(width_mm)/2 - (print_bb.center().x - print_bb.min.x);
|
||||
auto cy = scale_(height_mm)/2 - (print_bb.center().y - print_bb.min.y);
|
||||
|
||||
// Create the actual printer, forward any additional arguments to it.
|
||||
FilePrinter<format> printer(std::forward<Args>(args)...);
|
||||
printer.layers(layers.size()); // Allocate space for all the layers
|
||||
|
||||
int st_prev = 0;
|
||||
const std::string jobdesc = "Rasterizing and compressing sliced layers";
|
||||
set_status(0, jobdesc);
|
||||
tbb::spin_mutex m;
|
||||
|
||||
// Method that prints one layer
|
||||
auto process_layer = [this, &layers, &printer, &st_prev, &m, &jobdesc,
|
||||
print_bb, dir, cx, cy] (unsigned layer_id)
|
||||
{
|
||||
Layer& l = *(layers[layer_id]);
|
||||
|
||||
ExPolygonCollection slices = l.slices; // Copy the layer slices
|
||||
|
||||
// Sort the polygons in the layer
|
||||
std::stable_sort(slices.expolygons.begin(), slices.expolygons.end(),
|
||||
[](const ExPolygon& a, const ExPolygon& b) {
|
||||
return a.contour.contains(b.contour.first_point()) ? false : true;
|
||||
});
|
||||
|
||||
printer.beginLayer(layer_id); // Switch to the appropriate layer
|
||||
|
||||
// Draw all the polygons in the slice to the actual layer.
|
||||
std::for_each(l.object()->_shifted_copies.begin(),
|
||||
l.object()->_shifted_copies.end(),
|
||||
[&] (Point d)
|
||||
{
|
||||
std::for_each(slices.expolygons.begin(),
|
||||
slices.expolygons.end(),
|
||||
[&] (ExPolygon slice)
|
||||
{
|
||||
slice.translate(d.x, d.y);
|
||||
slice.translate(-print_bb.min.x + cx, -print_bb.min.y + cy);
|
||||
|
||||
printer.drawPolygon(slice, layer_id);
|
||||
});
|
||||
});
|
||||
|
||||
if(has_support_material() && layer_id > 0) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "support material for layer "
|
||||
<< layer_id << " defined but export is "
|
||||
"not yet implemented.";
|
||||
|
||||
}
|
||||
|
||||
printer.finishLayer(layer_id); // Finish the layer for later saving it.
|
||||
|
||||
auto st = static_cast<int>(layer_id*100.0/layers.size());
|
||||
m.lock();
|
||||
if( st - st_prev > 10) {
|
||||
set_status(st, jobdesc);
|
||||
st_prev = st;
|
||||
}
|
||||
m.unlock();
|
||||
|
||||
// printer.saveLayer(layer_id, dir); We could save the layer immediately
|
||||
};
|
||||
|
||||
// Print all the layers in parallel
|
||||
tbb::parallel_for<size_t, decltype(process_layer)>(0,
|
||||
layers.size(),
|
||||
process_layer);
|
||||
|
||||
// Sequential version (for testing)
|
||||
// for(unsigned l = 0; l < layers.size(); ++l) process_layer(l);
|
||||
|
||||
set_status(100, jobdesc);
|
||||
|
||||
// Save the print into the file system.
|
||||
set_status(0, "Writing layers to disk");
|
||||
printer.save(dir);
|
||||
set_status(100, "Writing layers completed");
|
||||
}
|
||||
|
||||
void Print::print_to_png(std::string dirpath, long width_px, long height_px,
|
||||
double width_mm, double height_mm) {
|
||||
print_to<FilePrinterFormat::PNG>(dirpath, width_mm, height_mm,
|
||||
width_px, height_px,
|
||||
width_mm, height_mm);
|
||||
void Print::print_to_png(std::string dirpath) {
|
||||
print_to<FilePrinterFormat::PNG>(*this, dirpath, 68.0, 120.0, 1440, 2560);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue