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
|
@ -159,6 +159,7 @@ add_library(libslic3r STATIC
|
||||||
${LIBDIR}/libslic3r/PolylineCollection.hpp
|
${LIBDIR}/libslic3r/PolylineCollection.hpp
|
||||||
${LIBDIR}/libslic3r/Print.cpp
|
${LIBDIR}/libslic3r/Print.cpp
|
||||||
${LIBDIR}/libslic3r/Print.hpp
|
${LIBDIR}/libslic3r/Print.hpp
|
||||||
|
${LIBDIR}/libslic3r/PrintExport.hpp
|
||||||
${LIBDIR}/libslic3r/PrintConfig.cpp
|
${LIBDIR}/libslic3r/PrintConfig.cpp
|
||||||
${LIBDIR}/libslic3r/PrintConfig.hpp
|
${LIBDIR}/libslic3r/PrintConfig.hpp
|
||||||
${LIBDIR}/libslic3r/PrintObject.cpp
|
${LIBDIR}/libslic3r/PrintObject.cpp
|
||||||
|
|
|
@ -12,19 +12,8 @@
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <boost/log/trivial.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 "slic3r/IProgressIndicator.hpp"
|
||||||
|
#include "PrintExport.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -1263,291 +1252,8 @@ void Print::set_status(int percent, const std::string &message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void Print::print_to_png(std::string dirpath) {
|
||||||
* Interface for a file printer of the slices. Implementation can be an SVG
|
print_to<FilePrinterFormat::PNG>(*this, dirpath, 68.0, 120.0, 1440, 2560);
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,26 +318,10 @@ public:
|
||||||
// Has the calculation been canceled?
|
// Has the calculation been canceled?
|
||||||
bool canceled() { return m_canceled; }
|
bool canceled() { return m_canceled; }
|
||||||
|
|
||||||
enum class FilePrinterFormat {
|
void print_to_png(std::string dirpath);
|
||||||
PNG,
|
|
||||||
SVG
|
|
||||||
};
|
|
||||||
|
|
||||||
template<FilePrinterFormat format, class...Args>
|
|
||||||
void print_to(std::string dirpath,
|
|
||||||
double width_mm,
|
|
||||||
double height_mm,
|
|
||||||
Args...args);
|
|
||||||
|
|
||||||
void print_to_png(std::string dirpath, long width_px, long height_px,
|
|
||||||
double width_mm, double height_mm);
|
|
||||||
|
|
||||||
void print_to_png(std::string dirpath) {
|
|
||||||
// Will need some GUI dialog perhaps for these to be specified.
|
|
||||||
print_to_png(dirpath, 1440, 2560, 68.0, 120.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||||
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
|
PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume);
|
||||||
|
|
||||||
|
|
366
xs/src/libslic3r/PrintExport.hpp
Normal file
366
xs/src/libslic3r/PrintExport.hpp
Normal file
|
@ -0,0 +1,366 @@
|
||||||
|
#ifndef PRINTEXPORT_HPP
|
||||||
|
#define PRINTEXPORT_HPP
|
||||||
|
|
||||||
|
#include "Print.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 <boost/log/trivial.hpp>
|
||||||
|
|
||||||
|
#include "Rasterizer/Rasterizer.hpp"
|
||||||
|
#include <tbb/parallel_for.h>
|
||||||
|
#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
enum class FilePrinterFormat {
|
||||||
|
PNG,
|
||||||
|
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:
|
||||||
|
|
||||||
|
void printConfig(const Print&);
|
||||||
|
|
||||||
|
// 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<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_;
|
||||||
|
const Print *print_ = nullptr;
|
||||||
|
|
||||||
|
std::string createIniContent(const std::string& projectname) {
|
||||||
|
double layer_height = print_?
|
||||||
|
print_->default_object_config.layer_height.getFloat() :
|
||||||
|
0.05;
|
||||||
|
|
||||||
|
return std::string(
|
||||||
|
"action = print\n"
|
||||||
|
"jobDir = ") + projectname + "\n" +
|
||||||
|
"expTime = 8.0\n"
|
||||||
|
"expTimeFirst = 35\n"
|
||||||
|
"stepNum = 40\n"
|
||||||
|
"wifiOn = 1\n"
|
||||||
|
"tiltSlow = 60\n"
|
||||||
|
"tiltFast = 15\n"
|
||||||
|
"numFade = 10\n"
|
||||||
|
"startdelay = 0\n"
|
||||||
|
"layerHeight = " + std::to_string(layer_height) + "\n"
|
||||||
|
"noteInfo = "
|
||||||
|
"expTime=8.0+resinType=FTD-IB-Black+layerHeight=0.05+printer=DWARF3\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline FilePrinter(double width_mm, double height_mm,
|
||||||
|
unsigned width_px, unsigned height_px,
|
||||||
|
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(); }
|
||||||
|
|
||||||
|
void printConfig(const Print& printconf) { print_ = &printconf; }
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
wxFileName filepath(path);
|
||||||
|
|
||||||
|
wxFFileOutputStream zipfile(path);
|
||||||
|
|
||||||
|
std::string project = filepath.GetName();
|
||||||
|
|
||||||
|
if(!zipfile.IsOk()) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! "
|
||||||
|
<< path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wxZipOutputStream zipstream(zipfile);
|
||||||
|
wxStdOutputStream pngstream(zipstream);
|
||||||
|
|
||||||
|
zipstream.PutNextEntry("config.ini");
|
||||||
|
pngstream << createIniContent(project);
|
||||||
|
|
||||||
|
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 = project + 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<FilePrinterFormat format, class...Args>
|
||||||
|
void print_to(Print& print,
|
||||||
|
std::string dirpath,
|
||||||
|
double width_mm,
|
||||||
|
double height_mm,
|
||||||
|
Args&&...args)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string& dir = dirpath;
|
||||||
|
|
||||||
|
// This map will hold the layers sorted by z coordinate. Layers on the
|
||||||
|
// same height (from different objects) will be mapped to the same key and
|
||||||
|
// rasterized to the same image.
|
||||||
|
std::map<long long, LayerPtrs> layers;
|
||||||
|
|
||||||
|
auto& objects = print.objects;
|
||||||
|
|
||||||
|
// Merge the sliced layers with the support layers
|
||||||
|
std::for_each(objects.begin(), objects.end(), [&layers](PrintObject *o) {
|
||||||
|
for(auto l : o->layers) {
|
||||||
|
auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
|
||||||
|
lyrs.push_back(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto l : o->support_layers) {
|
||||||
|
auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
|
||||||
|
lyrs.push_back(l);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto print_bb = print.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(width_mm, height_mm,
|
||||||
|
std::forward<Args>(args)...);
|
||||||
|
|
||||||
|
printer.printConfig(print);
|
||||||
|
|
||||||
|
printer.layers(layers.size()); // Allocate space for all the layers
|
||||||
|
|
||||||
|
int st_prev = 0;
|
||||||
|
const std::string jobdesc = "Rasterizing and compressing sliced layers";
|
||||||
|
print.set_status(0, jobdesc);
|
||||||
|
tbb::spin_mutex m;
|
||||||
|
|
||||||
|
std::vector<long long> keys;
|
||||||
|
keys.reserve(layers.size());
|
||||||
|
for(auto& e : layers) keys.push_back(e.first);
|
||||||
|
|
||||||
|
// Method that prints one layer
|
||||||
|
auto process_layer = [&layers, &keys, &printer, &st_prev, &m,
|
||||||
|
&jobdesc, print_bb, dir, cx, cy, &print] (unsigned layer_id)
|
||||||
|
{
|
||||||
|
LayerPtrs lrange = layers[keys[layer_id]];
|
||||||
|
|
||||||
|
printer.beginLayer(layer_id); // Switch to the appropriate layer
|
||||||
|
|
||||||
|
for(Layer *lp : lrange) {
|
||||||
|
Layer& l = *lp;
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// 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(print.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) {
|
||||||
|
print.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);
|
||||||
|
|
||||||
|
print.set_status(100, jobdesc);
|
||||||
|
|
||||||
|
// Save the print into the file system.
|
||||||
|
print.set_status(0, "Writing layers to disk");
|
||||||
|
printer.save(dir);
|
||||||
|
print.set_status(100, "Writing layers completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PRINTEXPORT_HPP
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <PrintConfig.hpp>
|
#include <PrintConfig.hpp>
|
||||||
#include <Print.hpp>
|
#include <Print.hpp>
|
||||||
|
#include <PrintExport.hpp>
|
||||||
#include <Model.hpp>
|
#include <Model.hpp>
|
||||||
#include <Utils.hpp>
|
#include <Utils.hpp>
|
||||||
|
|
||||||
|
@ -172,7 +173,7 @@ void PrintController::slice(PrintObject *pobj)
|
||||||
if(pobj->layers.empty())
|
if(pobj->layers.empty())
|
||||||
report_issue(IssueType::ERR,
|
report_issue(IssueType::ERR,
|
||||||
"No layers were detected. You might want to repair your "
|
"No layers were detected. You might want to repair your "
|
||||||
"STL file(s) or check their size or thickness and retry"
|
"STL file(s) or check their size or thickness and retry"
|
||||||
);
|
);
|
||||||
|
|
||||||
pobj->state.set_done(STEP_SLICE);
|
pobj->state.set_done(STEP_SLICE);
|
||||||
|
@ -268,8 +269,6 @@ void PrintController::slice()
|
||||||
|
|
||||||
void PrintController::slice_to_png()
|
void PrintController::slice_to_png()
|
||||||
{
|
{
|
||||||
assert(model_ != nullptr);
|
|
||||||
|
|
||||||
auto exd = query_png_export_data();
|
auto exd = query_png_export_data();
|
||||||
|
|
||||||
if(exd.zippath.empty()) return;
|
if(exd.zippath.empty()) return;
|
||||||
|
@ -287,8 +286,24 @@ void PrintController::slice_to_png()
|
||||||
print_->validate();
|
print_->validate();
|
||||||
} catch(std::exception& e) {
|
} catch(std::exception& e) {
|
||||||
report_issue(IssueType::ERR, e.what(), "Error");
|
report_issue(IssueType::ERR, e.what(), "Error");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/*bool correction = false;
|
||||||
|
if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) {
|
||||||
|
correction = true;
|
||||||
|
print_->invalidate_all_steps();
|
||||||
|
|
||||||
|
for(auto po : print_->objects) {
|
||||||
|
po->model_object()->scale(
|
||||||
|
Pointf3(exd.corr_x, exd.corr_y, exd.corr_z)
|
||||||
|
);
|
||||||
|
po->model_object()->invalidate_bounding_box();
|
||||||
|
po->reload_model_instances();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
auto print_bb = print_->bounding_box();
|
auto print_bb = print_->bounding_box();
|
||||||
|
|
||||||
// If the print does not fit into the print area we should cry about it.
|
// If the print does not fit into the print area we should cry about it.
|
||||||
|
@ -311,6 +326,7 @@ void PrintController::slice_to_png()
|
||||||
slice();
|
slice();
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
report_issue(IssueType::ERR, e.what(), "Exception");
|
report_issue(IssueType::ERR, e.what(), "Exception");
|
||||||
|
progress_indicator()->cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,12 +334,26 @@ void PrintController::slice_to_png()
|
||||||
print_->progressindicator = progress_indicator();
|
print_->progressindicator = progress_indicator();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
print_->print_to_png(exd.zippath, exd.width_px, exd.height_px,
|
print_to<FilePrinterFormat::PNG>( *print_, exd.zippath,
|
||||||
exd.width_mm, exd.height_mm);
|
exd.width_mm, exd.height_mm,
|
||||||
|
exd.width_px, exd.height_px );
|
||||||
|
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
report_issue(IssueType::ERR, e.what(), "Exception");
|
report_issue(IssueType::ERR, e.what(), "Exception");
|
||||||
|
progress_indicator()->cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*if(correction) { // scale the model back
|
||||||
|
print_->invalidate_all_steps();
|
||||||
|
for(auto po : print_->objects) {
|
||||||
|
po->model_object()->scale(
|
||||||
|
Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z)
|
||||||
|
);
|
||||||
|
po->model_object()->invalidate_bounding_box();
|
||||||
|
po->reload_model_instances();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
print_->progressindicator = pbak;
|
print_->progressindicator = pbak;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ public:
|
||||||
enum class IssueType {
|
enum class IssueType {
|
||||||
INFO,
|
INFO,
|
||||||
WARN,
|
WARN,
|
||||||
|
WARN_Q, // Warning with a question to continue
|
||||||
ERR,
|
ERR,
|
||||||
FATAL
|
FATAL
|
||||||
};
|
};
|
||||||
|
@ -91,7 +92,7 @@ public:
|
||||||
* @param brief A very brief description. Can be used for message dialog
|
* @param brief A very brief description. Can be used for message dialog
|
||||||
* title.
|
* title.
|
||||||
*/
|
*/
|
||||||
void report_issue(IssueType issuetype,
|
bool report_issue(IssueType issuetype,
|
||||||
const std::string& description,
|
const std::string& description,
|
||||||
const std::string& brief = "");
|
const std::string& brief = "");
|
||||||
|
|
||||||
|
@ -170,7 +171,9 @@ protected:
|
||||||
unsigned long width_px = 1440; // resolution - rows
|
unsigned long width_px = 1440; // resolution - rows
|
||||||
unsigned long height_px = 2560; // resolution columns
|
unsigned long height_px = 2560; // resolution columns
|
||||||
double width_mm = 68.0, height_mm = 120.0; // dimensions in mm
|
double width_mm = 68.0, height_mm = 120.0; // dimensions in mm
|
||||||
double corr = 1.0; // multiplies the dimensions
|
double corr_x = 1.0; // offsetting in x
|
||||||
|
double corr_y = 1.0; // offsetting in y
|
||||||
|
double corr_z = 1.0; // offsetting in y
|
||||||
};
|
};
|
||||||
|
|
||||||
// Should display a dialog with the input fields for printing to png
|
// Should display a dialog with the input fields for printing to png
|
||||||
|
@ -208,6 +211,9 @@ public:
|
||||||
* @brief Slice the print into zipped png files.
|
* @brief Slice the print into zipped png files.
|
||||||
*/
|
*/
|
||||||
void slice_to_png();
|
void slice_to_png();
|
||||||
|
|
||||||
|
|
||||||
|
void slice_to_png(std::string dirpath);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -60,19 +60,22 @@ AppControllerBoilerplate::query_destination_path(
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppControllerBoilerplate::report_issue(IssueType issuetype,
|
bool AppControllerBoilerplate::report_issue(IssueType issuetype,
|
||||||
const std::string &description,
|
const std::string &description,
|
||||||
const std::string &brief)
|
const std::string &brief)
|
||||||
{
|
{
|
||||||
auto icon = wxICON_INFORMATION;
|
auto icon = wxICON_INFORMATION;
|
||||||
|
auto style = wxOK|wxCENTRE;
|
||||||
switch(issuetype) {
|
switch(issuetype) {
|
||||||
case IssueType::INFO: break;
|
case IssueType::INFO: break;
|
||||||
case IssueType::WARN: icon = wxICON_WARNING; break;
|
case IssueType::WARN: icon = wxICON_WARNING; break;
|
||||||
|
case IssueType::WARN_Q: icon = wxICON_WARNING; style |= wxCANCEL; break;
|
||||||
case IssueType::ERR:
|
case IssueType::ERR:
|
||||||
case IssueType::FATAL: icon = wxICON_ERROR;
|
case IssueType::FATAL: icon = wxICON_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxMessageBox(description, brief, icon);
|
auto ret = wxMessageBox(description, brief, icon | style);
|
||||||
|
return ret != wxCANCEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
|
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
|
||||||
|
@ -143,6 +146,11 @@ public:
|
||||||
this, id_);
|
this, id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void cancel() override {
|
||||||
|
update(max(), "Abort");
|
||||||
|
IProgressIndicator::cancel();
|
||||||
|
}
|
||||||
|
|
||||||
virtual void state(float val) override {
|
virtual void state(float val) override {
|
||||||
if( val >= 1.0) state(static_cast<unsigned>(val));
|
if( val >= 1.0) state(static_cast<unsigned>(val));
|
||||||
}
|
}
|
||||||
|
@ -285,9 +293,12 @@ PrintController::PngExportData PrintController::query_png_export_data()
|
||||||
// Implement the logic of the PngExportDialog
|
// Implement the logic of the PngExportDialog
|
||||||
class PngExportView: public PngExportDialog {
|
class PngExportView: public PngExportDialog {
|
||||||
double ratio_, bs_ratio_;
|
double ratio_, bs_ratio_;
|
||||||
|
PrintController& ctl_;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
PngExportView(): PngExportDialog(wxTheApp->GetTopWindow()) {
|
PngExportView(PrintController& ctl):
|
||||||
|
PngExportDialog(wxTheApp->GetTopWindow()), ctl_(ctl)
|
||||||
|
{
|
||||||
ratio_ = double(spin_reso_width_->GetValue()) /
|
ratio_ = double(spin_reso_width_->GetValue()) /
|
||||||
spin_reso_height_->GetValue();
|
spin_reso_height_->GetValue();
|
||||||
|
|
||||||
|
@ -302,7 +313,9 @@ PrintController::PngExportData PrintController::query_png_export_data()
|
||||||
ret.height_px = spin_reso_height_->GetValue();
|
ret.height_px = spin_reso_height_->GetValue();
|
||||||
ret.width_mm = bed_width_spin_->GetValue();
|
ret.width_mm = bed_width_spin_->GetValue();
|
||||||
ret.height_mm = bed_height_spin_->GetValue();
|
ret.height_mm = bed_height_spin_->GetValue();
|
||||||
ret.corr = corr_spin_->GetValue();
|
ret.corr_x = corr_spin_x_->GetValue();
|
||||||
|
ret.corr_y = corr_spin_y_->GetValue();
|
||||||
|
ret.corr_z = corr_spin_z_->GetValue();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,7 +325,11 @@ PrintController::PngExportData PrintController::query_png_export_data()
|
||||||
spin_reso_height_->SetValue(data.height_px);
|
spin_reso_height_->SetValue(data.height_px);
|
||||||
bed_width_spin_->SetValue(data.width_mm);
|
bed_width_spin_->SetValue(data.width_mm);
|
||||||
bed_height_spin_->SetValue(data.height_mm);
|
bed_height_spin_->SetValue(data.height_mm);
|
||||||
corr_spin_->SetValue(data.corr);
|
corr_spin_x_->SetValue(data.corr_x);
|
||||||
|
corr_spin_y_->SetValue(data.corr_y);
|
||||||
|
corr_spin_z_->SetValue(data.corr_z);
|
||||||
|
if(data.zippath.empty()) export_btn_->Disable();
|
||||||
|
else export_btn_->Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void ResoLock( wxCommandEvent& /*event*/ ) override {
|
virtual void ResoLock( wxCommandEvent& /*event*/ ) override {
|
||||||
|
@ -352,9 +369,24 @@ PrintController::PngExportData PrintController::query_png_export_data()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void onFileChanged( wxFileDirPickerEvent& event ) {
|
||||||
|
if(filepick_ctl_->GetPath().IsEmpty()) export_btn_->Disable();
|
||||||
|
else export_btn_->Enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Close( wxCommandEvent& /*event*/ ) {
|
||||||
|
auto ret = wxID_OK;
|
||||||
|
|
||||||
|
if(wxFileName(filepick_ctl_->GetPath()).Exists())
|
||||||
|
if(!ctl_.report_issue(PrintController::IssueType::WARN_Q,
|
||||||
|
"File already exists. Overwrite?",
|
||||||
|
"Warning")) ret = wxID_CANCEL;
|
||||||
|
EndModal(ret);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PngExportView exdlg;
|
PngExportView exdlg(*this);
|
||||||
|
|
||||||
exdlg.prefill(prev_expdata_);
|
exdlg.prefill(prev_expdata_);
|
||||||
|
|
||||||
|
|
|
@ -23,12 +23,16 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin
|
||||||
bSizer16 = new wxBoxSizer( wxVERTICAL );
|
bSizer16 = new wxBoxSizer( wxVERTICAL );
|
||||||
|
|
||||||
wxGridSizer* gSizer2;
|
wxGridSizer* gSizer2;
|
||||||
gSizer2 = new wxGridSizer( 4, 1, 0, 0 );
|
gSizer2 = new wxGridSizer( 5, 1, 0, 0 );
|
||||||
|
|
||||||
filepick_text_ = new wxStaticText( this, wxID_ANY, _("Target zip file"), wxDefaultPosition, wxDefaultSize, 0 );
|
filepick_text_ = new wxStaticText( this, wxID_ANY, _("Target zip file"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||||
filepick_text_->Wrap( -1 );
|
filepick_text_->Wrap( -1 );
|
||||||
gSizer2->Add( filepick_text_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
gSizer2->Add( filepick_text_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||||
|
|
||||||
|
auto confpick_text = new wxStaticText( this, wxID_ANY, _("Config file (optional)"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||||
|
confpick_text->Wrap( -1 );
|
||||||
|
gSizer2->Add( confpick_text, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||||
|
|
||||||
resotext_ = new wxStaticText( this, wxID_ANY, _("Resolution (w, h) [px]"), wxDefaultPosition, wxDefaultSize, 0 );
|
resotext_ = new wxStaticText( this, wxID_ANY, _("Resolution (w, h) [px]"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||||
resotext_->Wrap( -1 );
|
resotext_->Wrap( -1 );
|
||||||
gSizer2->Add( resotext_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
gSizer2->Add( resotext_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||||
|
@ -37,7 +41,7 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin
|
||||||
bed_size_text_->Wrap( -1 );
|
bed_size_text_->Wrap( -1 );
|
||||||
gSizer2->Add( bed_size_text_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
gSizer2->Add( bed_size_text_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||||
|
|
||||||
corr_text_ = new wxStaticText( this, wxID_ANY, _("Size correction"), wxDefaultPosition, wxDefaultSize, 0 );
|
corr_text_ = new wxStaticText( this, wxID_ANY, _("Scale (x, y, z)"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||||
corr_text_->Wrap( -1 );
|
corr_text_->Wrap( -1 );
|
||||||
gSizer2->Add( corr_text_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
gSizer2->Add( corr_text_, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||||
|
|
||||||
|
@ -52,13 +56,16 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin
|
||||||
|
|
||||||
wxBoxSizer* filepick_layout_;
|
wxBoxSizer* filepick_layout_;
|
||||||
filepick_layout_ = new wxBoxSizer( wxHORIZONTAL );
|
filepick_layout_ = new wxBoxSizer( wxHORIZONTAL );
|
||||||
|
filepick_ctl_ = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString, _("Select a file"), wxT("*.zip"), wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_SAVE, wxDefaultValidator, wxT("filepick_ctl") );
|
||||||
filepick_ctl_ = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString, _("Select a file"), wxT("*.zip"), wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_SAVE | wxFLP_OVERWRITE_PROMPT, wxDefaultValidator, wxT("filepick_ctl") );
|
|
||||||
filepick_layout_->Add( filepick_ctl_, 2, wxALIGN_CENTER|wxALL, 5 );
|
filepick_layout_->Add( filepick_ctl_, 2, wxALIGN_CENTER|wxALL, 5 );
|
||||||
|
|
||||||
|
|
||||||
bSizer18->Add( filepick_layout_, 1, wxEXPAND, 5 );
|
bSizer18->Add( filepick_layout_, 1, wxEXPAND, 5 );
|
||||||
|
|
||||||
|
wxBoxSizer* confpick_layout_;
|
||||||
|
confpick_layout_ = new wxBoxSizer( wxHORIZONTAL );
|
||||||
|
confpick_ctl_ = new wxFilePickerCtrl( this, wxID_ANY, wxEmptyString, _("Select a file"), wxT("*.json"), wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL | wxFLP_DEFAULT_STYLE, wxDefaultValidator, wxT("filepick_ctl") );
|
||||||
|
confpick_layout_ ->Add( confpick_ctl_, 2, wxALIGN_CENTER|wxALL, 5 );
|
||||||
|
bSizer18->Add( confpick_layout_ , 1, wxEXPAND, 5 );
|
||||||
|
|
||||||
wxBoxSizer* resolution_layout_;
|
wxBoxSizer* resolution_layout_;
|
||||||
resolution_layout_ = new wxBoxSizer( wxHORIZONTAL );
|
resolution_layout_ = new wxBoxSizer( wxHORIZONTAL );
|
||||||
|
|
||||||
|
@ -104,9 +111,20 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin
|
||||||
wxBoxSizer* corr_layout_;
|
wxBoxSizer* corr_layout_;
|
||||||
corr_layout_ = new wxBoxSizer( wxHORIZONTAL );
|
corr_layout_ = new wxBoxSizer( wxHORIZONTAL );
|
||||||
|
|
||||||
corr_spin_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, 1, 0.1 );
|
corr_spin_x_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, 1, 0.01 );
|
||||||
corr_layout_->Add( corr_spin_, 0, wxALIGN_CENTER|wxALL, 5 );
|
corr_spin_x_->SetDigits(3);
|
||||||
|
corr_spin_x_->SetMaxSize(wxSize(100, -1));
|
||||||
|
corr_layout_->Add( corr_spin_x_, 0, wxALIGN_CENTER|wxALL, 5 );
|
||||||
|
|
||||||
|
corr_spin_y_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, 1, 0.01 );
|
||||||
|
corr_spin_y_->SetDigits(3);
|
||||||
|
corr_spin_y_->SetMaxSize(wxSize(100, -1));
|
||||||
|
corr_layout_->Add( corr_spin_y_, 0, wxALIGN_CENTER|wxALL, 5 );
|
||||||
|
|
||||||
|
corr_spin_z_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, 1, 0.01 );
|
||||||
|
corr_spin_z_->SetDigits(3);
|
||||||
|
corr_spin_z_->SetMaxSize(wxSize(100, -1));
|
||||||
|
corr_layout_->Add( corr_spin_z_, 0, wxALIGN_CENTER|wxALL, 5 );
|
||||||
|
|
||||||
corr_layout_->Add( 0, 0, 1, wxEXPAND, 5 );
|
corr_layout_->Add( 0, 0, 1, wxEXPAND, 5 );
|
||||||
|
|
||||||
|
@ -131,6 +149,7 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin
|
||||||
this->Centre( wxBOTH );
|
this->Centre( wxBOTH );
|
||||||
|
|
||||||
// Connect Events
|
// Connect Events
|
||||||
|
filepick_ctl_->Connect( wxEVT_COMMAND_FILEPICKER_CHANGED, wxFileDirPickerEventHandler( PngExportDialog::onFileChanged ), NULL, this );
|
||||||
spin_reso_width_->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
spin_reso_width_->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
||||||
spin_reso_height_->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
spin_reso_height_->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
||||||
reso_lock_btn_->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( PngExportDialog::ResoLock ), NULL, this );
|
reso_lock_btn_->Connect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( PngExportDialog::ResoLock ), NULL, this );
|
||||||
|
@ -143,6 +162,7 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin
|
||||||
PngExportDialog::~PngExportDialog()
|
PngExportDialog::~PngExportDialog()
|
||||||
{
|
{
|
||||||
// Disconnect Events
|
// Disconnect Events
|
||||||
|
filepick_ctl_->Disconnect( wxEVT_COMMAND_FILEPICKER_CHANGED, wxFileDirPickerEventHandler( PngExportDialog::onFileChanged ), NULL, this );
|
||||||
spin_reso_width_->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
spin_reso_width_->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
||||||
spin_reso_height_->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
spin_reso_height_->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this );
|
||||||
reso_lock_btn_->Disconnect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( PngExportDialog::ResoLock ), NULL, this );
|
reso_lock_btn_->Disconnect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( PngExportDialog::ResoLock ), NULL, this );
|
||||||
|
|
|
@ -41,16 +41,20 @@ class PngExportDialog : public wxDialog
|
||||||
wxStaticText* bed_size_text_;
|
wxStaticText* bed_size_text_;
|
||||||
wxStaticText* corr_text_;
|
wxStaticText* corr_text_;
|
||||||
wxFilePickerCtrl* filepick_ctl_;
|
wxFilePickerCtrl* filepick_ctl_;
|
||||||
|
wxFilePickerCtrl* confpick_ctl_;
|
||||||
wxSpinCtrl* spin_reso_width_;
|
wxSpinCtrl* spin_reso_width_;
|
||||||
wxSpinCtrl* spin_reso_height_;
|
wxSpinCtrl* spin_reso_height_;
|
||||||
wxToggleButton* reso_lock_btn_;
|
wxToggleButton* reso_lock_btn_;
|
||||||
wxSpinCtrlDouble* bed_width_spin_;
|
wxSpinCtrlDouble* bed_width_spin_;
|
||||||
wxSpinCtrlDouble* bed_height_spin_;
|
wxSpinCtrlDouble* bed_height_spin_;
|
||||||
wxToggleButton* bedsize_lock_btn_;
|
wxToggleButton* bedsize_lock_btn_;
|
||||||
wxSpinCtrlDouble* corr_spin_;
|
wxSpinCtrlDouble* corr_spin_x_;
|
||||||
|
wxSpinCtrlDouble* corr_spin_y_;
|
||||||
|
wxSpinCtrlDouble* corr_spin_z_;
|
||||||
wxButton* export_btn_;
|
wxButton* export_btn_;
|
||||||
|
|
||||||
// Virtual event handlers, overide them in your derived class
|
// Virtual event handlers, overide them in your derived class
|
||||||
|
virtual void onFileChanged( wxFileDirPickerEvent& event ) { event.Skip(); }
|
||||||
virtual void EvalResoSpin( wxCommandEvent& event ) { event.Skip(); }
|
virtual void EvalResoSpin( wxCommandEvent& event ) { event.Skip(); }
|
||||||
virtual void ResoLock( wxCommandEvent& event ) { event.Skip(); }
|
virtual void ResoLock( wxCommandEvent& event ) { event.Skip(); }
|
||||||
virtual void EvalBedSpin( wxCommandEvent& event ) { event.Skip(); }
|
virtual void EvalBedSpin( wxCommandEvent& event ) { event.Skip(); }
|
||||||
|
|
|
@ -15,7 +15,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float state_ = .0f, max_ = 1.f, step_;
|
float state_ = .0f, max_ = 1.f, step_;
|
||||||
std::function<void(void)> cancelfunc_ = [](){};
|
CancelFn cancelfunc_ = [](){};
|
||||||
unsigned proc_count_ = 1;
|
unsigned proc_count_ = 1;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -54,8 +54,11 @@ public:
|
||||||
/// Set up a cancel callback for the operation if feasible.
|
/// Set up a cancel callback for the operation if feasible.
|
||||||
inline void on_cancel(CancelFn func) { cancelfunc_ = func; }
|
inline void on_cancel(CancelFn func) { cancelfunc_ = func; }
|
||||||
|
|
||||||
/// Call a previously specified cancel callback.
|
/**
|
||||||
inline void on_cancel() { cancelfunc_(); }
|
* Explicitly shut down the progress indicator and call the associated
|
||||||
|
* callback.
|
||||||
|
*/
|
||||||
|
virtual void cancel() { cancelfunc_(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Set up how many subprocedures does the whole operation contain.
|
* \brief Set up how many subprocedures does the whole operation contain.
|
||||||
|
|
|
@ -208,7 +208,6 @@ _constant()
|
||||||
croak("%s\n", e.what());
|
croak("%s\n", e.what());
|
||||||
}
|
}
|
||||||
%};
|
%};
|
||||||
|
|
||||||
|
|
||||||
void print_to_png(std::string dirpath);
|
void print_to_png(std::string dirpath);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue