From ac9d81cfa059fa78250d04a309c9bde7e23daa90 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 27 Jun 2018 17:43:54 +0200 Subject: [PATCH] Refactor and extensions to png export dialog. --- xs/CMakeLists.txt | 1 + xs/src/libslic3r/Print.cpp | 300 +-------------------- xs/src/libslic3r/Print.hpp | 20 +- xs/src/libslic3r/PrintExport.hpp | 366 ++++++++++++++++++++++++++ xs/src/slic3r/AppController.cpp | 40 ++- xs/src/slic3r/AppController.hpp | 10 +- xs/src/slic3r/AppControllerWx.cpp | 44 +++- xs/src/slic3r/GUI/PngExportDialog.cpp | 36 ++- xs/src/slic3r/GUI/PngExportDialog.hpp | 6 +- xs/src/slic3r/IProgressIndicator.hpp | 9 +- xs/xsp/Print.xsp | 1 - 11 files changed, 492 insertions(+), 341 deletions(-) create mode 100644 xs/src/libslic3r/PrintExport.hpp diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index dee622a00..a00c19a81 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -159,6 +159,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/PolylineCollection.hpp ${LIBDIR}/libslic3r/Print.cpp ${LIBDIR}/libslic3r/Print.hpp + ${LIBDIR}/libslic3r/PrintExport.hpp ${LIBDIR}/libslic3r/PrintConfig.cpp ${LIBDIR}/libslic3r/PrintConfig.hpp ${LIBDIR}/libslic3r/PrintObject.cpp diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index e603b241f..6eb9ea97f 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -12,19 +12,8 @@ #include #include -// For png export of the sliced model -#include -#include - -#include -#include -#include - -#include "Rasterizer/Rasterizer.hpp" -#include -#include //#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 -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 { - - 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 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 -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 printer(std::forward(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(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(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(dirpath, width_mm, height_mm, - width_px, height_px, - width_mm, height_mm); +void Print::print_to_png(std::string dirpath) { + print_to(*this, dirpath, 68.0, 120.0, 1440, 2560); } } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 97aa7d4da..e66821e40 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -318,26 +318,10 @@ public: // Has the calculation been canceled? bool canceled() { return m_canceled; } - enum class FilePrinterFormat { - PNG, - SVG - }; - - template - 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); - } + void print_to_png(std::string dirpath); private: + bool invalidate_state_by_config_options(const std::vector &opt_keys); PrintRegionConfig _region_config_from_model_volume(const ModelVolume &volume); diff --git a/xs/src/libslic3r/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp new file mode 100644 index 000000000..9e8e19a15 --- /dev/null +++ b/xs/src/libslic3r/PrintExport.hpp @@ -0,0 +1,366 @@ +#ifndef PRINTEXPORT_HPP +#define PRINTEXPORT_HPP + +#include "Print.hpp" + +// For png export of the sliced model +#include +#include + +#include +#include +#include + +#include + +#include "Rasterizer/Rasterizer.hpp" +#include +#include //#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 +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 { + + 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 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 +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 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(scale_(l->print_z))]; + lyrs.push_back(l); + } + + for(auto l : o->support_layers) { + auto& lyrs = layers[static_cast(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 printer(width_mm, height_mm, + std::forward(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 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(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(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 diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp index 1ca6e8b3c..07ec8e39c 100644 --- a/xs/src/slic3r/AppController.cpp +++ b/xs/src/slic3r/AppController.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -172,7 +173,7 @@ void PrintController::slice(PrintObject *pobj) if(pobj->layers.empty()) report_issue(IssueType::ERR, "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); @@ -268,8 +269,6 @@ void PrintController::slice() void PrintController::slice_to_png() { - assert(model_ != nullptr); - auto exd = query_png_export_data(); if(exd.zippath.empty()) return; @@ -287,8 +286,24 @@ void PrintController::slice_to_png() print_->validate(); } catch(std::exception& e) { 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(); // 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(); } catch (std::exception& e) { report_issue(IssueType::ERR, e.what(), "Exception"); + progress_indicator()->cancel(); return; } @@ -318,12 +334,26 @@ void PrintController::slice_to_png() print_->progressindicator = progress_indicator(); try { - print_->print_to_png(exd.zippath, exd.width_px, exd.height_px, - exd.width_mm, exd.height_mm); + print_to( *print_, exd.zippath, + exd.width_mm, exd.height_mm, + exd.width_px, exd.height_px ); + } catch (std::exception& e) { 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; }); } diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp index 2d51ad51e..85e1f2e4d 100644 --- a/xs/src/slic3r/AppController.hpp +++ b/xs/src/slic3r/AppController.hpp @@ -51,6 +51,7 @@ public: enum class IssueType { INFO, WARN, + WARN_Q, // Warning with a question to continue ERR, FATAL }; @@ -91,7 +92,7 @@ public: * @param brief A very brief description. Can be used for message dialog * title. */ - void report_issue(IssueType issuetype, + bool report_issue(IssueType issuetype, const std::string& description, const std::string& brief = ""); @@ -170,7 +171,9 @@ protected: unsigned long width_px = 1440; // resolution - rows unsigned long height_px = 2560; // resolution columns 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 @@ -208,6 +211,9 @@ public: * @brief Slice the print into zipped png files. */ void slice_to_png(); + + + void slice_to_png(std::string dirpath); }; /** diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp index f417ae8c8..04acaff42 100644 --- a/xs/src/slic3r/AppControllerWx.cpp +++ b/xs/src/slic3r/AppControllerWx.cpp @@ -60,19 +60,22 @@ AppControllerBoilerplate::query_destination_path( return ret; } -void AppControllerBoilerplate::report_issue(IssueType issuetype, +bool AppControllerBoilerplate::report_issue(IssueType issuetype, const std::string &description, const std::string &brief) { auto icon = wxICON_INFORMATION; + auto style = wxOK|wxCENTRE; switch(issuetype) { case IssueType::INFO: break; case IssueType::WARN: icon = wxICON_WARNING; break; + case IssueType::WARN_Q: icon = wxICON_WARNING; style |= wxCANCEL; break; case IssueType::ERR: 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); @@ -143,6 +146,11 @@ public: this, id_); } + virtual void cancel() override { + update(max(), "Abort"); + IProgressIndicator::cancel(); + } + virtual void state(float val) override { if( val >= 1.0) state(static_cast(val)); } @@ -285,9 +293,12 @@ PrintController::PngExportData PrintController::query_png_export_data() // Implement the logic of the PngExportDialog class PngExportView: public PngExportDialog { double ratio_, bs_ratio_; + PrintController& ctl_; public: - PngExportView(): PngExportDialog(wxTheApp->GetTopWindow()) { + PngExportView(PrintController& ctl): + PngExportDialog(wxTheApp->GetTopWindow()), ctl_(ctl) + { ratio_ = double(spin_reso_width_->GetValue()) / spin_reso_height_->GetValue(); @@ -302,7 +313,9 @@ PrintController::PngExportData PrintController::query_png_export_data() ret.height_px = spin_reso_height_->GetValue(); ret.width_mm = bed_width_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; } @@ -312,7 +325,11 @@ PrintController::PngExportData PrintController::query_png_export_data() spin_reso_height_->SetValue(data.height_px); bed_width_spin_->SetValue(data.width_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 { @@ -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_); diff --git a/xs/src/slic3r/GUI/PngExportDialog.cpp b/xs/src/slic3r/GUI/PngExportDialog.cpp index 88b12d994..f0cd19c3c 100644 --- a/xs/src/slic3r/GUI/PngExportDialog.cpp +++ b/xs/src/slic3r/GUI/PngExportDialog.cpp @@ -23,12 +23,16 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin bSizer16 = new wxBoxSizer( wxVERTICAL ); 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_->Wrap( -1 ); 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_->Wrap( -1 ); 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 ); 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 ); 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_; 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 | wxFLP_OVERWRITE_PROMPT, wxDefaultValidator, wxT("filepick_ctl") ); + 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_layout_->Add( filepick_ctl_, 2, wxALIGN_CENTER|wxALL, 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_; resolution_layout_ = new wxBoxSizer( wxHORIZONTAL ); @@ -104,9 +111,20 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin wxBoxSizer* corr_layout_; corr_layout_ = new wxBoxSizer( wxHORIZONTAL ); - corr_spin_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, 1, 0.1 ); - corr_layout_->Add( corr_spin_, 0, wxALIGN_CENTER|wxALL, 5 ); + corr_spin_x_ = new wxSpinCtrlDouble( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, 1, 0.01 ); + 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 ); @@ -131,6 +149,7 @@ PngExportDialog::PngExportDialog( wxWindow* parent, wxWindowID id, const wxStrin this->Centre( wxBOTH ); // 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_height_->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), 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() { // 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_height_->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PngExportDialog::EvalResoSpin ), NULL, this ); reso_lock_btn_->Disconnect( wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler( PngExportDialog::ResoLock ), NULL, this ); diff --git a/xs/src/slic3r/GUI/PngExportDialog.hpp b/xs/src/slic3r/GUI/PngExportDialog.hpp index bcba1d5b7..bcd680999 100644 --- a/xs/src/slic3r/GUI/PngExportDialog.hpp +++ b/xs/src/slic3r/GUI/PngExportDialog.hpp @@ -41,16 +41,20 @@ class PngExportDialog : public wxDialog wxStaticText* bed_size_text_; wxStaticText* corr_text_; wxFilePickerCtrl* filepick_ctl_; + wxFilePickerCtrl* confpick_ctl_; wxSpinCtrl* spin_reso_width_; wxSpinCtrl* spin_reso_height_; wxToggleButton* reso_lock_btn_; wxSpinCtrlDouble* bed_width_spin_; wxSpinCtrlDouble* bed_height_spin_; wxToggleButton* bedsize_lock_btn_; - wxSpinCtrlDouble* corr_spin_; + wxSpinCtrlDouble* corr_spin_x_; + wxSpinCtrlDouble* corr_spin_y_; + wxSpinCtrlDouble* corr_spin_z_; wxButton* export_btn_; // 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 ResoLock( wxCommandEvent& event ) { event.Skip(); } virtual void EvalBedSpin( wxCommandEvent& event ) { event.Skip(); } diff --git a/xs/src/slic3r/IProgressIndicator.hpp b/xs/src/slic3r/IProgressIndicator.hpp index 1c7c61236..73296697f 100644 --- a/xs/src/slic3r/IProgressIndicator.hpp +++ b/xs/src/slic3r/IProgressIndicator.hpp @@ -15,7 +15,7 @@ public: private: float state_ = .0f, max_ = 1.f, step_; - std::function cancelfunc_ = [](){}; + CancelFn cancelfunc_ = [](){}; unsigned proc_count_ = 1; public: @@ -54,8 +54,11 @@ public: /// Set up a cancel callback for the operation if feasible. 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. diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 43f286c2f..d0fb6c2d8 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -208,7 +208,6 @@ _constant() croak("%s\n", e.what()); } %}; - void print_to_png(std::string dirpath);