From 95b0467c850f801d7de0e391f93d618843c93eb4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 18:51:27 +0100 Subject: [PATCH 01/15] Reusing unified polygons from statistics --- src/libslic3r/PrintExport.hpp | 4 +- src/libslic3r/Rasterizer/Rasterizer.cpp | 14 +-- src/libslic3r/Rasterizer/Rasterizer.hpp | 4 +- src/libslic3r/SLAPrint.cpp | 130 +++++++++++------------- src/libslic3r/SLAPrint.hpp | 17 +++- 5 files changed, 80 insertions(+), 89 deletions(-) diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index df9446cf52..003ed5af4d 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -43,7 +43,7 @@ class FilePrinter { public: // Draw an ExPolygon which is a polygon inside a slice on the specified layer. - void draw_polygon(const ExPolygon& p, unsigned lyr); + void draw_polygon(const Polygon& p, unsigned lyr); // Tell the printer how many layers should it consider. void layers(unsigned layernum); @@ -209,7 +209,7 @@ public: inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - inline void draw_polygon(const ExPolygon& p, unsigned lyr) { + inline void draw_polygon(const Polygon& p, unsigned lyr) { assert(lyr < m_layers_rst.size()); m_layers_rst[lyr].first.draw(p); } diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/Rasterizer/Rasterizer.cpp index 5961d9b789..3e42e37d8b 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.cpp +++ b/src/libslic3r/Rasterizer/Rasterizer.cpp @@ -1,5 +1,5 @@ #include "Rasterizer.hpp" -#include +#include #include @@ -72,22 +72,16 @@ public: clear(); } - void draw(const ExPolygon &poly) { + void draw(const Polygon &poly) { agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 scanlines; - auto&& path = to_path(poly.contour); + auto&& path = to_path(poly); if(m_o == Origin::TOP_LEFT) flipy(path); ras.add_path(path); - for(auto h : poly.holes) { - auto&& holepath = to_path(h); - if(m_o == Origin::TOP_LEFT) flipy(holepath); - ras.add_path(holepath); - } - agg::render_scanlines(ras, scanlines, m_renderer); } @@ -169,7 +163,7 @@ void Raster::clear() m_impl->clear(); } -void Raster::draw(const ExPolygon &poly) +void Raster::draw(const Polygon &poly) { assert(m_impl); m_impl->draw(poly); diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp index 06d5b88c6d..05ffd99b3b 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/src/libslic3r/Rasterizer/Rasterizer.hpp @@ -6,7 +6,7 @@ namespace Slic3r { -class ExPolygon; +class Polygon; /** * @brief Raster captures an anti-aliased monochrome canvas where vectorial @@ -83,7 +83,7 @@ public: void clear(); /// Draw a polygon with holes. - void draw(const ExPolygon& poly); + void draw(const Polygon& poly); /// Save the raster on the specified stream. void save(std::ostream& stream, Compression comp = Compression::RAW); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index ee24cf3ecf..af3f20a594 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -915,19 +915,7 @@ void SLAPrint::process() report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); }; - auto fillstats = [this]() { - - m_print_statistics.clear(); - - // Fill statistics - fill_statistics(); - - report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); - }; - - // Rasterizing the model objects, and their supports - auto rasterize = [this, max_objstatus, ilhs]() { - if(canceled()) return; + auto fillstats = [this, ilhs]() { // clear the rasterizer input m_printer_input.clear(); @@ -962,6 +950,18 @@ void SLAPrint::process() } } + m_print_statistics.clear(); + + // Fill statistics + fill_statistics(); + + report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); + }; + + // Rasterizing the model objects, and their supports + auto rasterize = [this, max_objstatus]() { + if(canceled()) return; + // collect all the keys // If the raster has vertical orientation, we will flip the coordinates @@ -1015,28 +1015,30 @@ void SLAPrint::process() // Switch to the appropriate layer in the printer printer.begin_layer(level_id); - using Instance = SLAPrintObject::Instance; - - auto draw = - [&printer, flpXY, level_id](ExPolygon& poly, const Instance& tr) - { - poly.rotate(double(tr.rotation)); - poly.translate(tr.shift(X), tr.shift(Y)); - if(flpXY) swapXY(poly); + for(const Polygon& poly : printlayer.transformed_slices()) printer.draw_polygon(poly, level_id); - }; - for(const SliceRecord& sr : printlayer.slices()) { - if(! sr.print_obj()) continue; - for(const Instance& inst : sr.print_obj()->instances()) { - ExPolygons objsl = sr.get_slice(soModel); - for(ExPolygon& poly : objsl) draw(poly, inst); +// auto draw = +// [&printer, flpXY, level_id](Polygon& poly, const Instance& tr) +// { +// poly.rotate(double(tr.rotation)); +// poly.translate(tr.shift(X), tr.shift(Y)); +// if(flpXY) for(auto& p : poly.points) std::swap(p(X), p(Y)); +// printer.draw_polygon(poly, level_id); +// }; - ExPolygons supsl = sr.get_slice(soSupport); - for(ExPolygon& poly : supsl) draw(poly, inst); - } - } +// for(const SliceRecord& sr : printlayer.slices()) { +// if(! sr.print_obj()) continue; + +// for(const Instance& inst : sr.print_obj()->instances()) { +// ExPolygons objsl = sr.get_slice(soModel); +// for(ExPolygon& poly : objsl) draw(poly, inst); + +// ExPolygons supsl = sr.get_slice(soSupport); +// for(ExPolygon& poly : supsl) draw(poly, inst); +// } +// } // Finish the layer for later saving it. printer.finish_layer(level_id); @@ -1217,9 +1219,6 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector& instances) { + auto get_all_polygons = + [flpXY](const ExPolygons& input_polygons, + const std::vector& instances) + { const size_t inst_cnt = instances.size(); size_t polygon_cnt = 0; @@ -1249,6 +1255,7 @@ void SLAPrint::fill_statistics() ExPolygon tmp = polygon; tmp.rotate(double(instances[i].rotation)); tmp.translate(instances[i].shift.x(), instances[i].shift.y()); + if(flpXY) swapXY(tmp); polygons_append(polygons, to_polygons(std::move(tmp))); } } @@ -1263,55 +1270,30 @@ void SLAPrint::fill_statistics() size_t slow_layers = 0; size_t fast_layers = 0; - // find highest object - // Which is a better bet? To compare by max_z or by number of layers in the index? - // float max_z = 0.; - size_t max_layers_cnt = 0; - size_t highest_obj_idx = 0; - for (SLAPrintObject *&po : m_objects) { - auto& slice_index = po->get_slice_index(); - if (! slice_index.empty()) { - // float z = (-- slice_index.end())->slice_level(); - size_t cnt = slice_index.size(); - //if (z > max_z) { - if (cnt > max_layers_cnt) { - max_layers_cnt = cnt; - // max_z = z; - highest_obj_idx = &po - &m_objects.front(); - } - } - } - - const SLAPrintObject * highest_obj = m_objects[highest_obj_idx]; - auto& highest_obj_slice_index = highest_obj->get_slice_index(); - const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); double fade_layer_time = init_exp_time; int sliced_layer_cnt = 0; - for (const SliceRecord& layer : highest_obj_slice_index) + for (PrintLayer& layer : m_printer_input) { - const auto l_height = double(layer.layer_height()); + if(layer.slices().empty()) continue; + + // Layer height should match for all object slices for a given level. + const auto l_height = double(layer.slices().front().get().layer_height()); // Calculation of the consumed material Polygons model_polygons; Polygons supports_polygons; - for (SLAPrintObject * po : m_objects) - { - const SliceRecord *record = nullptr; - { - const SliceRecord& slr = po->closest_slice_to_slice_level(layer.slice_level(), float(EPSILON)); - if (!slr.is_valid()) continue; - record = &slr; - } + for(const SliceRecord& record : layer.slices()) { + const SLAPrintObject *po = record.print_obj(); - const ExPolygons &modelslices = record->get_slice(soModel); + const ExPolygons &modelslices = record.get_slice(soModel); if (!modelslices.empty()) append(model_polygons, get_all_polygons(modelslices, po->instances())); - const ExPolygons &supportslices = record->get_slice(soSupport); + const ExPolygons &supportslices = record.get_slice(soSupport); if (!supportslices.empty()) append(supports_polygons, get_all_polygons(supportslices, po->instances())); } @@ -1321,7 +1303,7 @@ void SLAPrint::fill_statistics() for (const Polygon& polygon : model_polygons) layer_model_area += polygon.area(); - if (layer_model_area != 0) + if (layer_model_area < 0 || layer_model_area > 0) models_volume += layer_model_area * l_height; if (!supports_polygons.empty() && !model_polygons.empty()) @@ -1330,9 +1312,13 @@ void SLAPrint::fill_statistics() for (const Polygon& polygon : supports_polygons) layer_support_area += polygon.area(); - if (layer_support_area != 0) + if (layer_support_area < 0 || layer_model_area > 0) supports_volume += layer_support_area * l_height; + // Here we can save the expensively calculated polygons for printing + append(model_polygons, supports_polygons); + layer.transformed_slices(union_(model_polygons)); + // Calculation of the slow and fast layers to the future controlling those values on FW const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; @@ -1365,7 +1351,7 @@ void SLAPrint::fill_statistics() // Estimated printing time // A layers count o the highest object - if (max_layers_cnt == 0) + if (m_printer_input.size() == 0) m_print_statistics.estimated_print_time = "N/A"; else m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time)); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index afdfab4156..9b6c896878 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -127,7 +127,7 @@ public: bool is_valid() const { return ! std::isnan(m_slice_z); } - const SLAPrintObject* print_obj() const { return m_po; } + const SLAPrintObject* print_obj() const { assert(m_po); return m_po; } // Methods for setting the indices into the slice vectors. void set_model_slice_idx(const SLAPrintObject &po, size_t id) { @@ -328,6 +328,7 @@ class SLAPrint : public PrintBaseWithState private: // Prevents erroneous use by other classes. typedef PrintBaseWithState Inherited; + void fill_statistics(); public: // An aggregation of SliceRecord-s from all the print objects for each @@ -339,6 +340,14 @@ public: // The collection of slice records for the current level. std::vector> m_slices; + Polygons m_transformed_slices; + + template void transformed_slices(Container&& c) { + m_transformed_slices = std::forward(c); + } + + friend void SLAPrint::fill_statistics(); + public: explicit PrintLayer(coord_t lvl) : m_level(lvl) {} @@ -353,6 +362,10 @@ public: coord_t level() const { return m_level; } auto slices() const -> const decltype (m_slices)& { return m_slices; } + + const Polygons& transformed_slices() const { + return m_transformed_slices; + } }; SLAPrint(): m_stepmask(slapsCount, true) {} @@ -399,8 +412,6 @@ private: // Invalidate steps based on a set of parameters changed. bool invalidate_state_by_config_options(const std::vector &opt_keys); - void fill_statistics(); - SLAPrintConfig m_print_config; SLAPrinterConfig m_printer_config; SLAMaterialConfig m_material_config; From bc747615135c5c6661b530ae57fd0e370e178f94 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 27 Mar 2019 10:59:29 +0100 Subject: [PATCH 02/15] Integrating new step, removing old and unused steps. --- src/libslic3r/SLAPrint.cpp | 83 +++++++++++++++------------------- src/libslic3r/SLAPrint.hpp | 19 ++++---- src/slic3r/GUI/GLCanvas3D.cpp | 4 +- src/slic3r/GUI/GUI_Preview.cpp | 2 +- 4 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index ee24cf3ecf..862913f951 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -43,8 +43,7 @@ const std::array OBJ_STEP_LEVELS = 30, // slaposSupportPoints, 25, // slaposSupportTree, 25, // slaposBasePool, - 5, // slaposSliceSupports, - 5 // slaposIndexSlices + 10, // slaposSliceSupports, }; const std::array OBJ_STEP_LABELS = @@ -54,22 +53,19 @@ const std::array OBJ_STEP_LABELS = L("Generating support tree"), // slaposSupportTree, L("Generating pad"), // slaposBasePool, L("Slicing supports"), // slaposSliceSupports, - L("Slicing supports") // slaposIndexSlices, }; // Should also add up to 100 (%) const std::array PRINT_STEP_LEVELS = { 5, // slapsStats - 94, // slapsRasterize - 1, // slapsValidate + 95, // slapsRasterize }; const std::array PRINT_STEP_LABELS = { - L("Calculating statistics"), // slapsStats + L("Merging slices and calculating statistics"), // slapsStats L("Rasterizing layers"), // slapsRasterize - L("Validating"), // slapsValidate }; } @@ -206,7 +202,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); } else if (model_object_list_extended(m_model, model)) { // Add new objects. Their volumes and configs will be synchronized later. - update_apply_status(this->invalidate_step(slapsRasterize)); + update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval)); for (const ModelObject *model_object : m_model.objects) model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) { @@ -218,7 +214,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf // Reorder the objects, add new objects. // First stop background processing before shuffling or deleting the PrintObjects in the object list. this->call_cancel_callback(); - update_apply_status(this->invalidate_step(slapsRasterize)); + update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval)); // Second create a new list of objects. std::vector model_objects_old(std::move(m_model.objects)); m_model.objects.clear(); @@ -390,7 +386,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf if (new_instances != it_print_object_status->print_object->instances()) { // Instances changed. it_print_object_status->print_object->set_instances(new_instances); - update_apply_status(this->invalidate_step(slapsRasterize)); + update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval)); } print_objects_new.emplace_back(it_print_object_status->print_object); const_cast(*it_print_object_status).status = PrintObjectStatus::Reused; @@ -613,6 +609,18 @@ std::string SLAPrint::validate() const return ""; } +bool SLAPrint::invalidate_step(SLAPrintStep step) +{ + bool invalidated = Inherited::invalidate_step(step); + + // propagate to dependent steps + if (step == slapsMergeSlicesAndEval) { + invalidated |= this->invalidate_all_steps(); + } + + return invalidated; +} + template void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args) { @@ -639,7 +647,7 @@ void SLAPrint::process() const size_t objcount = m_objects.size(); const unsigned min_objstatus = 0; // where the per object operations start - const unsigned max_objstatus = PRINT_STEP_LEVELS[slapsRasterize]; // where the per object operations end + const unsigned max_objstatus = PRINT_STEP_LEVELS[slapsMergeSlicesAndEval]; // where the per object operations end // the coefficient that multiplies the per object status values which // are set up for <0, 100>. They need to be scaled into the whole process @@ -883,7 +891,7 @@ void SLAPrint::process() // Slicing the support geometries similarly to the model slicing procedure. // If the pad had been added previously (see step "base_pool" than it will // be part of the slices) - auto slice_supports = [](SLAPrintObject& po) { + auto slice_supports = [this](SLAPrintObject& po) { auto& sd = po.m_supportdata; if(sd) sd->support_slices.clear(); @@ -906,16 +914,14 @@ void SLAPrint::process() { po.m_slice_index[i].set_support_slice_idx(po, i); } - }; - // We have the layer polygon collection but we need to unite them into - // an index where the key is the height level in discrete levels (clipper) - auto index_slices = [this/*, ilhd*/](SLAPrintObject& /*po*/) { // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices. report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); }; - auto fillstats = [this]() { + // Merging the slices from all the print objects into one slice grid and + // calculating print statistics from the merge result. + auto merge_slices_and_eval_stats = [this]() { m_print_statistics.clear(); @@ -1079,15 +1085,13 @@ void SLAPrint::process() support_points, support_tree, base_pool, - slice_supports, - index_slices + slice_supports }; std::array print_program = { - fillstats, - rasterize, - [](){} // validate + merge_slices_and_eval_stats, + rasterize }; unsigned st = min_objstatus; @@ -1127,7 +1131,7 @@ void SLAPrint::process() } std::array printsteps = { - slapsStats, slapsRasterize, slapsValidate + slapsMergeSlicesAndEval, slapsRasterize }; // this would disable the rasterization step @@ -1193,11 +1197,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps(); } else if (step == slaposSupportPoints) { - invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices }); - invalidated |= m_print->invalidate_step(slapsRasterize); + invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } else if (step == slaposSupportTree) { - invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports, slaposIndexSlices }); - invalidated |= m_print->invalidate_step(slapsRasterize); + invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports }); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } else if (step == slaposBasePool) { - invalidated |= this->invalidate_steps({slaposSliceSupports, slaposIndexSlices}); - invalidated |= m_print->invalidate_step(slapsRasterize); + invalidated |= this->invalidate_steps({slaposSliceSupports}); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } else if (step == slaposSliceSupports) { - invalidated |= this->invalidate_step(slaposIndexSlices); - invalidated |= m_print->invalidate_step(slapsRasterize); - } else if(step == slaposIndexSlices) { - invalidated |= m_print->invalidate_step(slapsRasterize); + invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval); } return invalidated; } @@ -1547,18 +1548,6 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const return idx >= v.size() ? EMPTY_SLICE : v[idx]; } -const std::vector & SLAPrintObject::get_slice_index() const -{ - // assert(is_step_done(slaposIndexSlices)); - return m_slice_index; -} - -const std::vector &SLAPrintObject::get_model_slices() const -{ - // assert(is_step_done(slaposObjectSlice)); - return m_model_slices; -} - bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const { switch (step) { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index afdfab4156..0909645f7a 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -11,9 +11,8 @@ namespace Slic3r { enum SLAPrintStep : unsigned int { - slapsStats, - slapsRasterize, - slapsValidate, + slapsMergeSlicesAndEval, + slapsRasterize, slapsCount }; @@ -22,8 +21,7 @@ enum SLAPrintObjectStep : unsigned int { slaposSupportPoints, slaposSupportTree, slaposBasePool, - slaposSliceSupports, - slaposIndexSlices, + slaposSliceSupports, slaposCount }; @@ -190,7 +188,7 @@ private: return it; } - const std::vector& get_model_slices() const; + const std::vector& get_model_slices() const { return m_model_slices; } const std::vector& get_support_slices() const; public: @@ -205,7 +203,9 @@ public: // ///////////////////////////////////////////////////////////////////////// // Retrieve the slice index. - const std::vector& get_slice_index() const; + const std::vector& get_slice_index() const { + return m_slice_index; + } // Search slice index for the closest slice to given print_level. // max_epsilon gives the allowable deviation of the returned slice record's @@ -370,7 +370,7 @@ public: // Returns true if an object step is done on all objects and there's at least one object. bool is_step_done(SLAPrintObjectStep step) const; // Returns true if the last step was finished with success. - bool finished() const override { return this->is_step_done(slaposIndexSlices) && this->Inherited::is_step_done(slapsRasterize); } + bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); } template void export_raster(const std::string& fname) { if(m_printer) m_printer->save(fname); @@ -396,6 +396,9 @@ private: using SLAPrinter = FilePrinter; using SLAPrinterPtr = std::unique_ptr; + // Implement same logic as in SLAPrintObject + bool invalidate_step(SLAPrintStep st); + // Invalidate steps based on a set of parameters changed. bool invalidate_state_by_config_options(const std::vector &opt_keys); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index da62ddab08..eaa5f30f59 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5010,7 +5010,7 @@ void GLCanvas3D::_render_sla_slices() const } if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) && - obj->is_step_done(slaposIndexSlices) && !obj->get_slice_index().empty()) + obj->is_step_done(slaposSliceSupports) && !obj->get_slice_index().empty()) { double layer_height = print->default_object_config().layer_height.value; double initial_layer_height = print->material_config().initial_layer_height.value; @@ -6212,7 +6212,7 @@ void GLCanvas3D::_load_shells_sla() int obj_idx = 0; for (const SLAPrintObject* obj : print->objects()) { - if (!obj->is_step_done(slaposIndexSlices)) + if (!obj->is_step_done(slaposSliceSupports)) continue; unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 18324f2e8a..8ed6ccb804 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -772,7 +772,7 @@ void Preview::load_print_as_sla() std::vector zs; double initial_layer_height = print->material_config().initial_layer_height.value; for (const SLAPrintObject* obj : print->objects()) - if (obj->is_step_done(slaposIndexSlices) && !obj->get_slice_index().empty()) + if (obj->is_step_done(slaposSliceSupports) && !obj->get_slice_index().empty()) { auto low_coord = obj->get_slice_index().front().print_level(); for (auto& rec : obj->get_slice_index()) From 440e54181b48291e9eed5298c80893661afb4c4c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 27 Mar 2019 18:37:50 +0100 Subject: [PATCH 03/15] Output raster seem ok, stats broken. --- .../libnest2d/backends/clipper/CMakeLists.txt | 1 + .../backends/clipper/clipper_polygon.hpp | 72 +++ .../libnest2d/backends/clipper/geometries.hpp | 121 +---- src/libnest2d/tests/printer_parts.h | 30 +- src/libslic3r/ModelArrange.cpp | 2 +- src/libslic3r/PrintExport.hpp | 12 +- src/libslic3r/Rasterizer/Rasterizer.cpp | 45 +- src/libslic3r/Rasterizer/Rasterizer.hpp | 7 +- src/libslic3r/SLAPrint.cpp | 417 +++++++++++------- src/libslic3r/SLAPrint.hpp | 76 ++-- 10 files changed, 436 insertions(+), 347 deletions(-) create mode 100644 src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp diff --git a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt index 995afcc76d..462d1dd068 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt +++ b/src/libnest2d/include/libnest2d/backends/clipper/CMakeLists.txt @@ -64,6 +64,7 @@ endif() target_include_directories(ClipperBackend INTERFACE ${Boost_INCLUDE_DIRS} ) target_sources(ClipperBackend INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/clipper_polygon.hpp ${SRC_DIR}/libnest2d/utils/boost_alg.hpp ) target_compile_definitions(ClipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp b/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp new file mode 100644 index 0000000000..e9fbfbd18a --- /dev/null +++ b/src/libnest2d/include/libnest2d/backends/clipper/clipper_polygon.hpp @@ -0,0 +1,72 @@ +#ifndef CLIPPER_POLYGON_HPP +#define CLIPPER_POLYGON_HPP + +#include + +namespace ClipperLib { + +struct Polygon { + Path Contour; + Paths Holes; + + inline Polygon() = default; + + inline explicit Polygon(const Path& cont): Contour(cont) {} + inline explicit Polygon(const Paths& holes): + Holes(holes) {} + inline Polygon(const Path& cont, const Paths& holes): + Contour(cont), Holes(holes) {} + + inline explicit Polygon(Path&& cont): Contour(std::move(cont)) {} + inline explicit Polygon(Paths&& holes): Holes(std::move(holes)) {} + inline Polygon(Path&& cont, Paths&& holes): + Contour(std::move(cont)), Holes(std::move(holes)) {} +}; + +inline IntPoint& operator +=(IntPoint& p, const IntPoint& pa ) { + // This could be done with SIMD + p.X += pa.X; + p.Y += pa.Y; + return p; +} + +inline IntPoint operator+(const IntPoint& p1, const IntPoint& p2) { + IntPoint ret = p1; + ret += p2; + return ret; +} + +inline IntPoint& operator -=(IntPoint& p, const IntPoint& pa ) { + p.X -= pa.X; + p.Y -= pa.Y; + return p; +} + +inline IntPoint operator -(IntPoint& p ) { + IntPoint ret = p; + ret.X = -ret.X; + ret.Y = -ret.Y; + return ret; +} + +inline IntPoint operator-(const IntPoint& p1, const IntPoint& p2) { + IntPoint ret = p1; + ret -= p2; + return ret; +} + +inline IntPoint& operator *=(IntPoint& p, const IntPoint& pa ) { + p.X *= pa.X; + p.Y *= pa.Y; + return p; +} + +inline IntPoint operator*(const IntPoint& p1, const IntPoint& p2) { + IntPoint ret = p1; + ret *= p2; + return ret; +} + +} + +#endif // CLIPPER_POLYGON_HPP diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 9f881e7e0d..2c9550cf8c 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -10,84 +10,15 @@ #include #include -#include - -namespace ClipperLib { -using PointImpl = IntPoint; -using PathImpl = Path; -using HoleStore = std::vector; - -struct PolygonImpl { - PathImpl Contour; - HoleStore Holes; - - inline PolygonImpl() = default; - - inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {} - inline explicit PolygonImpl(const HoleStore& holes): - Holes(holes) {} - inline PolygonImpl(const Path& cont, const HoleStore& holes): - Contour(cont), Holes(holes) {} - - inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {} - inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {} - inline PolygonImpl(Path&& cont, HoleStore&& holes): - Contour(std::move(cont)), Holes(std::move(holes)) {} -}; - -inline PointImpl& operator +=(PointImpl& p, const PointImpl& pa ) { - // This could be done with SIMD - p.X += pa.X; - p.Y += pa.Y; - return p; -} - -inline PointImpl operator+(const PointImpl& p1, const PointImpl& p2) { - PointImpl ret = p1; - ret += p2; - return ret; -} - -inline PointImpl& operator -=(PointImpl& p, const PointImpl& pa ) { - p.X -= pa.X; - p.Y -= pa.Y; - return p; -} - -inline PointImpl operator -(PointImpl& p ) { - PointImpl ret = p; - ret.X = -ret.X; - ret.Y = -ret.Y; - return ret; -} - -inline PointImpl operator-(const PointImpl& p1, const PointImpl& p2) { - PointImpl ret = p1; - ret -= p2; - return ret; -} - -inline PointImpl& operator *=(PointImpl& p, const PointImpl& pa ) { - p.X *= pa.X; - p.Y *= pa.Y; - return p; -} - -inline PointImpl operator*(const PointImpl& p1, const PointImpl& p2) { - PointImpl ret = p1; - ret *= p2; - return ret; -} - -} +#include "clipper_polygon.hpp" namespace libnest2d { // Aliases for convinience -using ClipperLib::PointImpl; -using ClipperLib::PathImpl; -using ClipperLib::PolygonImpl; -using ClipperLib::HoleStore; +using PointImpl = ClipperLib::IntPoint; +using PathImpl = ClipperLib::Path; +using HoleStore = ClipperLib::Paths; +using PolygonImpl = ClipperLib::Polygon; // Type of coordinate units used by Clipper template<> struct CoordType { @@ -158,33 +89,24 @@ template<> inline TCoord& y(PointImpl& p) #define DISABLE_BOOST_AREA namespace _smartarea { + template inline double area(const PolygonImpl& /*sh*/) { return std::nan(""); } template<> -inline double area(const PolygonImpl& sh) { - double a = 0; - - std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h) - { - a -= ClipperLib::Area(h); +inline double area(const PolygonImpl& sh) { + return std::accumulate(sh.Holes.begin(), sh.Holes.end(), + ClipperLib::Area(sh.Contour), + [](double a, const ClipperLib::Path& pt){ + return a + ClipperLib::Area(pt); }); - - return -ClipperLib::Area(sh.Contour) + a; } template<> -inline double area(const PolygonImpl& sh) { - double a = 0; - - std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h) - { - a += ClipperLib::Area(h); - }); - - return ClipperLib::Area(sh.Contour) + a; +inline double area(const PolygonImpl& sh) { + return -area(sh); } } @@ -390,11 +312,17 @@ inline void rotate(PolygonImpl& sh, const Radians& rads) } // namespace shapelike #define DISABLE_BOOST_NFP_MERGE -inline std::vector _merge(ClipperLib::Clipper& clipper) { +inline std::vector clipper_execute( + ClipperLib::Clipper& clipper, + ClipperLib::ClipType clipType, + ClipperLib::PolyFillType subjFillType = ClipperLib::pftEvenOdd, + ClipperLib::PolyFillType clipFillType = ClipperLib::pftEvenOdd) +{ shapelike::Shapes retv; ClipperLib::PolyTree result; - clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative); + clipper.Execute(clipType, result, subjFillType, clipFillType); + retv.reserve(static_cast(result.Total())); std::function processHole; @@ -437,15 +365,12 @@ merge(const std::vector& shapes) for(auto& path : shapes) { valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); - - for(auto& hole : path.Holes) { - valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed); - } + valid &= clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); } if(!valid) throw GeometryException(GeomErr::MERGE); - return _merge(clipper); + return clipper_execute(clipper, ClipperLib::ctUnion, ClipperLib::pftNegative); } } diff --git a/src/libnest2d/tests/printer_parts.h b/src/libnest2d/tests/printer_parts.h index b9a4eb8fab..1e65826bd3 100644 --- a/src/libnest2d/tests/printer_parts.h +++ b/src/libnest2d/tests/printer_parts.h @@ -2,36 +2,10 @@ #define PRINTER_PARTS_H #include -#include - -#ifndef CLIPPER_BACKEND_HPP -namespace ClipperLib { -using PointImpl = IntPoint; -using PathImpl = Path; -using HoleStore = std::vector; - -struct PolygonImpl { - PathImpl Contour; - HoleStore Holes; - - inline PolygonImpl() {} - - inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {} - inline explicit PolygonImpl(const HoleStore& holes): - Holes(holes) {} - inline PolygonImpl(const Path& cont, const HoleStore& holes): - Contour(cont), Holes(holes) {} - - inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {} - inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {} - inline PolygonImpl(Path&& cont, HoleStore&& holes): - Contour(std::move(cont)), Holes(std::move(holes)) {} -}; -} -#endif +#include using TestData = std::vector; -using TestDataEx = std::vector; +using TestDataEx = std::vector; extern const TestData PRINTER_PART_POLYGONS; extern const TestData STEGOSAUR_POLYGONS; diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 4942e35b60..54627ba86b 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -574,7 +574,7 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { for(ModelInstance* objinst : objptr->instances) { if(objinst) { - ClipperLib::PolygonImpl pn; + ClipperLib::Polygon pn; pn.Contour = clpath; // Efficient conversion to item. diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index 003ed5af4d..c0c12209a3 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -42,8 +42,9 @@ template class FilePrinter { public: - // Draw an ExPolygon which is a polygon inside a slice on the specified layer. - void draw_polygon(const Polygon& p, unsigned lyr); + // Draw a polygon which is a polygon inside a slice on the specified layer. + void draw_polygon(const ExPolygon& p, unsigned lyr); + void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr); // Tell the printer how many layers should it consider. void layers(unsigned layernum); @@ -209,7 +210,12 @@ public: inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); } inline unsigned layers() const { return unsigned(m_layers_rst.size()); } - inline void draw_polygon(const Polygon& p, unsigned lyr) { + inline void draw_polygon(const ExPolygon& p, unsigned lyr) { + assert(lyr < m_layers_rst.size()); + m_layers_rst[lyr].first.draw(p); + } + + inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) { assert(lyr < m_layers_rst.size()); m_layers_rst[lyr].first.draw(p); } diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/Rasterizer/Rasterizer.cpp index 3e42e37d8b..45c9daf3b4 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.cpp +++ b/src/libslic3r/Rasterizer/Rasterizer.cpp @@ -1,5 +1,6 @@ #include "Rasterizer.hpp" -#include +#include +#include #include @@ -72,7 +73,7 @@ public: clear(); } - void draw(const Polygon &poly) { + template inline void draw(const Geometry &poly) { agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 scanlines; @@ -104,14 +105,36 @@ private: return p(1) * SCALING_FACTOR/m_pxdim.h_mm; } - agg::path_storage to_path(const Polygon& poly) { + agg::path_storage to_path(const Polygon& poly) + { agg::path_storage path; + auto it = poly.points.begin(); path.move_to(getPx(*it), getPy(*it)); - while(++it != poly.points.end()) + while(++it != poly.points.end()) path.line_to(getPx(*it), getPy(*it)); + path.line_to(getPx(poly.points.front()), getPy(poly.points.front())); + + return path; + } + + + double getPx(const ClipperLib::IntPoint& p) { + return p.X * SCALING_FACTOR/m_pxdim.w_mm; + } + + double getPy(const ClipperLib::IntPoint& p) { + return p.Y * SCALING_FACTOR/m_pxdim.h_mm; + } + + agg::path_storage to_path(const ClipperLib::Path& poly) + { + agg::path_storage path; + auto it = poly.begin(); + path.move_to(getPx(*it), getPy(*it)); + while(++it != poly.end()) path.line_to(getPx(*it), getPy(*it)); - path.line_to(getPx(poly.points.front()), getPy(poly.points.front())); + path.line_to(getPx(poly.front()), getPy(poly.front())); return path; } @@ -163,10 +186,16 @@ void Raster::clear() m_impl->clear(); } -void Raster::draw(const Polygon &poly) +void Raster::draw(const ExPolygon &expoly) { - assert(m_impl); - m_impl->draw(poly); + m_impl->draw(expoly.contour); + for(auto& h : expoly.holes) m_impl->draw(h); +} + +void Raster::draw(const ClipperLib::Polygon &poly) +{ + m_impl->draw(poly.Contour); + for(auto& h : poly.Holes) m_impl->draw(h); } void Raster::save(std::ostream& stream, Compression comp) diff --git a/src/libslic3r/Rasterizer/Rasterizer.hpp b/src/libslic3r/Rasterizer/Rasterizer.hpp index 05ffd99b3b..0468b66444 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.hpp +++ b/src/libslic3r/Rasterizer/Rasterizer.hpp @@ -4,9 +4,11 @@ #include #include +namespace ClipperLib { class Polygon; } + namespace Slic3r { -class Polygon; +class ExPolygon; /** * @brief Raster captures an anti-aliased monochrome canvas where vectorial @@ -83,7 +85,8 @@ public: void clear(); /// Draw a polygon with holes. - void draw(const Polygon& poly); + void draw(const ExPolygon& poly); + void draw(const ClipperLib::Polygon& poly); /// Save the raster on the specified stream. void save(std::ostream& stream, Compression comp = Compression::RAW); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index ab7feed0e6..eaa4642b0c 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -12,6 +12,9 @@ #include #include +// For geometry algorithms with native Clipper types (no copies and conversions) +#include + //#include //#include "tbb/mutex.h" #include "I18N.hpp" @@ -958,8 +961,249 @@ void SLAPrint::process() m_print_statistics.clear(); - // Fill statistics - fill_statistics(); + using ClipperPolygon = libnest2d::PolygonImpl; + using ClipperPath = ClipperLib::Path; + using ClipperPoint = ClipperLib::IntPoint; + using ClipperPolygons = std::vector; + using libnest2d::Radians; + namespace sl = libnest2d::shapelike; + + // If the raster has vertical orientation, we will flip the coordinates + bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait; + + auto polyunion = [] (const ClipperPolygons& subjects) + { + ClipperLib::Clipper clipper; + + bool closed = true; + + for(auto& path : subjects) { + clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); + } + + auto mode = ClipperLib::pftPositive; + + return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); + }; + + auto polydiff = [](const ClipperPolygons& subjects, const ClipperPolygons& clips) + { + ClipperLib::Clipper clipper; + + bool closed = true; + + for(auto& path : subjects) { + clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); + } + + for(auto& path : clips) { + clipper.AddPath(path.Contour, ClipperLib::ptClip, closed); + clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed); + } + + auto mode = ClipperLib::pftPositive; + + return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); + }; + + auto area = [](const ClipperPolygon& poly) + { + using ClipperLib::Area; + return std::accumulate( poly.Holes.begin(), poly.Holes.end(), + Area(poly.Contour), + [](double a, const ClipperPath& p) { return a + Area(p); }); + }; + + const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); + const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0; + const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0; + + const double init_exp_time = m_material_config.initial_exposure_time.getFloat(); + const double exp_time = m_material_config.exposure_time.getFloat(); + + const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20] + + const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR; + const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR; + const double display_area = width*height; + + // get polygons for all instances in the object + auto get_all_polygons = + [flpXY](const ExPolygons& input_polygons, + const std::vector& instances) + { + ClipperPolygons polygons; + polygons.reserve(input_polygons.size() * instances.size()); + + for (const ExPolygon& polygon : input_polygons) { + if(polygon.contour.empty()) continue; + + for (size_t i = 0; i < instances.size(); ++i) + { + ClipperPolygon poly; + + // should be a move + poly.Contour.reserve(polygon.contour.size() + 1); + + for(auto& p : polygon.contour.points) + poly.Contour.emplace_back(p.x(), p.y()); + + auto pfirst = poly.Contour.front(); + poly.Contour.emplace_back(pfirst); + + for(auto& h : polygon.holes) { + poly.Holes.emplace_back(); + auto& hole = poly.Holes.back(); + hole.reserve(h.points.size() + 1); + + for(auto& p : h.points) hole.emplace_back(p.x(), p.y()); + auto pfirst = hole.front(); hole.emplace_back(pfirst); + } + + sl::rotate(poly, Radians(double(instances[i].rotation))); + sl::translate(poly, ClipperPoint{instances[i].shift(X), + instances[i].shift(Y)}); + if (flpXY) { + for(auto& p : poly.Contour) std::swap(p.X, p.Y); + std::reverse(poly.Contour.begin(), poly.Contour.end()); + + for(auto& h : poly.Holes) { + for(auto& p : h) std::swap(p.X, p.Y); + std::reverse(h.begin(), h.end()); + } + } + + polygons.emplace_back(std::move(poly)); + } + } + return polygons; + }; + + double supports_volume = 0.0; + double models_volume = 0.0; + + double estim_time = 0.0; + + size_t slow_layers = 0; + size_t fast_layers = 0; + + const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); + double fade_layer_time = init_exp_time; + + int sliced_layer_cnt = 0; + for (PrintLayer& layer : m_printer_input) + { + // vector of slice record references + auto& lyrslices = layer.slices(); + + if(lyrslices.empty()) continue; + + // Layer height should match for all object slices for a given level. + const auto l_height = double(lyrslices.front().get().layer_height()); + + // Calculation of the consumed material + + ClipperPolygons model_polygons; + ClipperPolygons supports_polygons; + + size_t c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) { + return a + sr.get_slice(soModel).size(); + }); + + model_polygons.reserve(c); + + c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) { + return a + sr.get_slice(soModel).size(); + }); + + supports_polygons.reserve(c); + + for(const SliceRecord& record : layer.slices()) { + const SLAPrintObject *po = record.print_obj(); + + const ExPolygons &modelslices = record.get_slice(soModel); + if (!modelslices.empty()) { + ClipperPolygons v = get_all_polygons(modelslices, po->instances()); + for(ClipperPolygon& p_tmp : v) model_polygons.emplace_back(std::move(p_tmp)); + } + + const ExPolygons &supportslices = record.get_slice(soSupport); + if (!supportslices.empty()) { + ClipperPolygons v = get_all_polygons(supportslices, po->instances()); + for(ClipperPolygon& p_tmp : v) supports_polygons.emplace_back(std::move(p_tmp)); + } + } + + model_polygons = polyunion(model_polygons); + double layer_model_area = 0; + for (const ClipperPolygon& polygon : model_polygons) + layer_model_area += area(polygon); + + if (layer_model_area < 0 || layer_model_area > 0) + models_volume += layer_model_area * l_height; + + if(!supports_polygons.empty()) { + if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); + else supports_polygons = polydiff(supports_polygons, model_polygons); + // allegedly, union of subject is done withing the diff + } + + double layer_support_area = 0; + for (const ClipperPolygon& polygon : supports_polygons) + layer_support_area += area(polygon); + + if (layer_support_area < 0 || layer_model_area > 0) + supports_volume += layer_support_area * l_height; + + // Here we can save the expensively calculated polygons for printing + ClipperPolygons trslices; + trslices.reserve(model_polygons.size() + supports_polygons.size()); + for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); + for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); + + layer.transformed_slices(polyunion(trslices)); + + // Calculation of the slow and fast layers to the future controlling those values on FW + + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; + const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; + if (is_fast_layer) + fast_layers++; + else + slow_layers++; + + + // Calculation of the printing time + + if (sliced_layer_cnt < 3) + estim_time += init_exp_time; + else if (fade_layer_time > exp_time) + { + fade_layer_time -= delta_fade_time; + estim_time += fade_layer_time; + } + else + estim_time += exp_time; + + estim_time += tilt_time; + + sliced_layer_cnt++; + } + + m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR; + m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR; + + // Estimated printing time + // A layers count o the highest object + if (m_printer_input.size() == 0) + m_print_statistics.estimated_print_time = "N/A"; + else + m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time)); + + m_print_statistics.fast_layers_count = fast_layers; + m_print_statistics.slow_layers_count = slow_layers; report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW); }; @@ -1011,7 +1255,7 @@ void SLAPrint::process() // procedure to process one height level. This will run in parallel auto lvlfn = - [this, &slck, &printer, slot, sd, ist, &pst, flpXY] + [this, &slck, &printer, slot, sd, ist, &pst] (unsigned level_id) { if(canceled()) return; @@ -1021,31 +1265,9 @@ void SLAPrint::process() // Switch to the appropriate layer in the printer printer.begin_layer(level_id); - for(const Polygon& poly : printlayer.transformed_slices()) + for(const ClipperLib::Polygon& poly : printlayer.transformed_slices()) printer.draw_polygon(poly, level_id); - -// auto draw = -// [&printer, flpXY, level_id](Polygon& poly, const Instance& tr) -// { -// poly.rotate(double(tr.rotation)); -// poly.translate(tr.shift(X), tr.shift(Y)); -// if(flpXY) for(auto& p : poly.points) std::swap(p(X), p(Y)); -// printer.draw_polygon(poly, level_id); -// }; - -// for(const SliceRecord& sr : printlayer.slices()) { -// if(! sr.print_obj()) continue; - -// for(const Instance& inst : sr.print_obj()->instances()) { -// ExPolygons objsl = sr.get_slice(soModel); -// for(ExPolygon& poly : objsl) draw(poly, inst); - -// ExPolygons supsl = sr.get_slice(soSupport); -// for(ExPolygon& poly : supsl) draw(poly, inst); -// } -// } - // Finish the layer for later saving it. printer.finish_layer(level_id); @@ -1221,149 +1443,6 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector& instances) - { - const size_t inst_cnt = instances.size(); - - size_t polygon_cnt = 0; - for (const ExPolygon& polygon : input_polygons) - polygon_cnt += polygon.holes.size() + 1; - - Polygons polygons; - polygons.reserve(polygon_cnt * inst_cnt); - for (const ExPolygon& polygon : input_polygons) { - for (size_t i = 0; i < inst_cnt; ++i) - { - ExPolygon tmp = polygon; - tmp.rotate(double(instances[i].rotation)); - tmp.translate(instances[i].shift.x(), instances[i].shift.y()); - if(flpXY) swapXY(tmp); - polygons_append(polygons, to_polygons(std::move(tmp))); - } - } - return polygons; - }; - - double supports_volume = 0.0; - double models_volume = 0.0; - - double estim_time = 0.0; - - size_t slow_layers = 0; - size_t fast_layers = 0; - - const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); - double fade_layer_time = init_exp_time; - - int sliced_layer_cnt = 0; - for (PrintLayer& layer : m_printer_input) - { - if(layer.slices().empty()) continue; - - // Layer height should match for all object slices for a given level. - const auto l_height = double(layer.slices().front().get().layer_height()); - - // Calculation of the consumed material - - Polygons model_polygons; - Polygons supports_polygons; - - for(const SliceRecord& record : layer.slices()) { - const SLAPrintObject *po = record.print_obj(); - - const ExPolygons &modelslices = record.get_slice(soModel); - if (!modelslices.empty()) - append(model_polygons, get_all_polygons(modelslices, po->instances())); - - const ExPolygons &supportslices = record.get_slice(soSupport); - if (!supportslices.empty()) - append(supports_polygons, get_all_polygons(supportslices, po->instances())); - } - - model_polygons = union_(model_polygons); - double layer_model_area = 0; - for (const Polygon& polygon : model_polygons) - layer_model_area += polygon.area(); - - if (layer_model_area < 0 || layer_model_area > 0) - models_volume += layer_model_area * l_height; - - if (!supports_polygons.empty() && !model_polygons.empty()) - supports_polygons = diff(supports_polygons, model_polygons); - double layer_support_area = 0; - for (const Polygon& polygon : supports_polygons) - layer_support_area += polygon.area(); - - if (layer_support_area < 0 || layer_model_area > 0) - supports_volume += layer_support_area * l_height; - - // Here we can save the expensively calculated polygons for printing - append(model_polygons, supports_polygons); - layer.transformed_slices(union_(model_polygons)); - - // Calculation of the slow and fast layers to the future controlling those values on FW - - const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; - const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; - if (is_fast_layer) - fast_layers++; - else - slow_layers++; - - - // Calculation of the printing time - - if (sliced_layer_cnt < 3) - estim_time += init_exp_time; - else if (fade_layer_time > exp_time) - { - fade_layer_time -= delta_fade_time; - estim_time += fade_layer_time; - } - else - estim_time += exp_time; - - estim_time += tilt_time; - - sliced_layer_cnt++; - } - - m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR; - m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR; - - // Estimated printing time - // A layers count o the highest object - if (m_printer_input.size() == 0) - m_print_statistics.estimated_print_time = "N/A"; - else - m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time)); - - m_print_statistics.fast_layers_count = fast_layers; - m_print_statistics.slow_layers_count = slow_layers; -} - // Returns true if an object step is done on all objects and there's at least one object. bool SLAPrint::is_step_done(SLAPrintObjectStep step) const { diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index e645a9574b..0688307710 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -6,6 +6,7 @@ #include "PrintExport.hpp" #include "Point.hpp" #include "MTUtils.hpp" +#include #include namespace Slic3r { @@ -328,46 +329,8 @@ class SLAPrint : public PrintBaseWithState private: // Prevents erroneous use by other classes. typedef PrintBaseWithState Inherited; - void fill_statistics(); public: - // An aggregation of SliceRecord-s from all the print objects for each - // occupied layer. Slice record levels dont have to match exactly. - // They are unified if the level difference is within +/- SCALED_EPSILON - class PrintLayer { - coord_t m_level; - - // The collection of slice records for the current level. - std::vector> m_slices; - - Polygons m_transformed_slices; - - template void transformed_slices(Container&& c) { - m_transformed_slices = std::forward(c); - } - - friend void SLAPrint::fill_statistics(); - - public: - - explicit PrintLayer(coord_t lvl) : m_level(lvl) {} - - // for being sorted in their container (see m_printer_input) - bool operator<(const PrintLayer& other) const { - return m_level < other.m_level; - } - - void add(const SliceRecord& sr) { m_slices.emplace_back(sr); } - - coord_t level() const { return m_level; } - - auto slices() const -> const decltype (m_slices)& { return m_slices; } - - const Polygons& transformed_slices() const { - return m_transformed_slices; - } - }; - SLAPrint(): m_stepmask(slapsCount, true) {} virtual ~SLAPrint() override { this->clear(); } @@ -401,6 +364,43 @@ public: std::string validate() const override; + // An aggregation of SliceRecord-s from all the print objects for each + // occupied layer. Slice record levels dont have to match exactly. + // They are unified if the level difference is within +/- SCALED_EPSILON + class PrintLayer { + coord_t m_level; + + // The collection of slice records for the current level. + std::vector> m_slices; + + std::vector m_transformed_slices; + + template void transformed_slices(Container&& c) { + m_transformed_slices = std::forward(c); + } + + friend void SLAPrint::process(); + + public: + + explicit PrintLayer(coord_t lvl) : m_level(lvl) {} + + // for being sorted in their container (see m_printer_input) + bool operator<(const PrintLayer& other) const { + return m_level < other.m_level; + } + + void add(const SliceRecord& sr) { m_slices.emplace_back(sr); } + + coord_t level() const { return m_level; } + + auto slices() const -> const decltype (m_slices)& { return m_slices; } + + const std::vector & transformed_slices() const { + return m_transformed_slices; + } + }; + // The aggregated and leveled print records from various objects. // TODO: use this structure for the preview in the future. const std::vector& print_layers() const { return m_printer_input; } From 50c351e0f4bf498d4890b652074103569145bddf Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 28 Mar 2019 11:01:41 +0100 Subject: [PATCH 04/15] Fix broken arrange --- .../libnest2d/backends/clipper/geometries.hpp | 24 ++++++++++++------- src/libnest2d/include/libnest2d/libnest2d.hpp | 2 +- .../include/libnest2d/placers/nfpplacer.hpp | 6 ++--- .../libnest2d/placers/placer_boilerplate.hpp | 4 ++-- .../libnest2d/selections/djd_heuristic.hpp | 2 +- .../include/libnest2d/utils/boost_alg.hpp | 2 +- 6 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 2c9550cf8c..d0cc448964 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -150,9 +150,10 @@ template<> inline void offset(PolygonImpl& sh, TCoord distance) // but throwing would be an overkill. Instead, we should warn the // caller about the inability to create correct geometries if(!found_the_contour) { - sh.Contour = r; + sh.Contour = std::move(r); ClipperLib::ReversePath(sh.Contour); - sh.Contour.push_back(sh.Contour.front()); + auto front_p = sh.Contour.front(); + sh.Contour.emplace_back(std::move(front_p)); found_the_contour = true; } else { dout() << "Warning: offsetting result is invalid!"; @@ -162,9 +163,10 @@ template<> inline void offset(PolygonImpl& sh, TCoord distance) // TODO If there are multiple contours we can't be sure which hole // belongs to the first contour. (But in this case the situation is // bad enough to let it go...) - sh.Holes.push_back(r); + sh.Holes.emplace_back(std::move(r)); ClipperLib::ReversePath(sh.Holes.back()); - sh.Holes.back().push_back(sh.Holes.back().front()); + auto front_p = sh.Holes.back().front(); + sh.Holes.back().emplace_back(std::move(front_p)); } } } @@ -328,16 +330,18 @@ inline std::vector clipper_execute( std::function processHole; auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) { - PolygonImpl poly(pptr->Contour); - poly.Contour.push_back(poly.Contour.front()); + PolygonImpl poly; + poly.Contour.swap(pptr->Contour); auto front_p = poly.Contour.front(); + poly.Contour.emplace_back(std::move(front_p)); for(auto h : pptr->Childs) { processHole(h, poly); } retv.push_back(poly); }; processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly) { - poly.Holes.push_back(pptr->Contour); - poly.Holes.back().push_back(poly.Holes.back().front()); + poly.Holes.emplace_back(std::move(pptr->Contour)); + auto front_p = poly.Holes.back().front(); + poly.Holes.back().emplace_back(std::move(front_p)); for(auto c : pptr->Childs) processPoly(c); }; @@ -365,7 +369,9 @@ merge(const std::vector& shapes) for(auto& path : shapes) { valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); - valid &= clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); + + for(auto& h : path.Holes) + valid &= clipper.AddPath(h, ClipperLib::ptSubject, closed); } if(!valid) throw GeometryException(GeomErr::MERGE); diff --git a/src/libnest2d/include/libnest2d/libnest2d.hpp b/src/libnest2d/include/libnest2d/libnest2d.hpp index 49baa65f25..c7b252e5d5 100644 --- a/src/libnest2d/include/libnest2d/libnest2d.hpp +++ b/src/libnest2d/include/libnest2d/libnest2d.hpp @@ -966,7 +966,7 @@ private: for(size_t i = 0; i < pckgrp.size(); i++) { auto items = pckgrp[i]; - pg.push_back({}); + pg.emplace_back(); pg[i].reserve(items.size()); for(Item& itemA : items) { diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 6fb717a7a6..91affe9786 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -261,7 +261,7 @@ template class EdgeCache { while(next != endit) { contour_.emap.emplace_back(*(first++), *(next++)); contour_.full_distance += contour_.emap.back().length(); - contour_.distances.push_back(contour_.full_distance); + contour_.distances.emplace_back(contour_.full_distance); } } @@ -276,10 +276,10 @@ template class EdgeCache { while(next != endit) { hc.emap.emplace_back(*(first++), *(next++)); hc.full_distance += hc.emap.back().length(); - hc.distances.push_back(hc.full_distance); + hc.distances.emplace_back(hc.full_distance); } - holes_.push_back(hc); + holes_.emplace_back(std::move(hc)); } } diff --git a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp index 309a5007de..abcd861830 100644 --- a/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/placers/placer_boilerplate.hpp @@ -63,7 +63,7 @@ public: bool pack(Item& item, const Range& rem = Range()) { auto&& r = static_cast(this)->trypack(item, rem); if(r) { - items_.push_back(*(r.item_ptr_)); + items_.emplace_back(*(r.item_ptr_)); farea_valid_ = false; } return r; @@ -78,7 +78,7 @@ public: if(r) { r.item_ptr_->translation(r.move_); r.item_ptr_->rotation(r.rot_); - items_.push_back(*(r.item_ptr_)); + items_.emplace_back(*(r.item_ptr_)); farea_valid_ = false; } } diff --git a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp index b03534dc46..25007e580e 100644 --- a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp +++ b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp @@ -667,7 +667,7 @@ public: addBin(); ItemList& not_packed = not_packeds[b]; for(unsigned idx = b; idx < store_.size(); idx+=bincount_guess) { - not_packed.push_back(store_[idx]); + not_packed.emplace_back(store_[idx]); } } diff --git a/src/libnest2d/include/libnest2d/utils/boost_alg.hpp b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp index a6988ca007..baf1c6a105 100644 --- a/src/libnest2d/include/libnest2d/utils/boost_alg.hpp +++ b/src/libnest2d/include/libnest2d/utils/boost_alg.hpp @@ -463,7 +463,7 @@ template<> inline std::string serialize( auto& v = *it; hf.emplace_back(getX(v)*scale, getY(v)*scale); }; - holes.push_back(hf); + holes.emplace_back(std::move(hf)); } Polygonf poly; From b19d411738979ec9416827aec52d494489ba5363 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 29 Mar 2019 13:34:22 +0100 Subject: [PATCH 05/15] It seems that stats are not broken after all. --- src/libslic3r/SLAPrint.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index eaa4642b0c..ba6e5a904a 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -578,11 +578,6 @@ sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) { return pcfg; } - -void swapXY(ExPolygon& expoly) { - for(auto& p : expoly.contour.points) std::swap(p(X), p(Y)); - for(auto& h : expoly.holes) for(auto& p : h.points) std::swap(p(X), p(Y)); -} } std::string SLAPrint::validate() const @@ -1008,13 +1003,7 @@ void SLAPrint::process() return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); }; - auto area = [](const ClipperPolygon& poly) - { - using ClipperLib::Area; - return std::accumulate( poly.Holes.begin(), poly.Holes.end(), - Area(poly.Contour), - [](double a, const ClipperPath& p) { return a + Area(p); }); - }; + auto area = [](const ClipperPolygon& poly) { return - sl::area(poly); }; const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0; From 65934218028121fdb6f8a494122a64f17778cd66 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 29 Mar 2019 17:20:19 +0100 Subject: [PATCH 06/15] fix for statistics --- src/libslic3r/PrintExport.hpp | 2 +- src/libslic3r/SLAPrint.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/PrintExport.hpp b/src/libslic3r/PrintExport.hpp index 5b0a911773..04b993a529 100644 --- a/src/libslic3r/PrintExport.hpp +++ b/src/libslic3r/PrintExport.hpp @@ -224,7 +224,7 @@ public: inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) { assert(lyr < m_layers_rst.size()); - m_layers_rst[lyr].first.draw(p); + m_layers_rst[lyr].raster.draw(p); } inline void begin_layer(unsigned lyr) { diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index ba6e5a904a..9e03b6f842 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1143,7 +1143,7 @@ void SLAPrint::process() for (const ClipperPolygon& polygon : supports_polygons) layer_support_area += area(polygon); - if (layer_support_area < 0 || layer_model_area > 0) + if (layer_support_area < 0 || layer_support_area > 0) supports_volume += layer_support_area * l_height; // Here we can save the expensively calculated polygons for printing From 4eb5d91a8f2209f5b5a192a80dd7f03905a3aa10 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 1 Apr 2019 11:37:26 +0200 Subject: [PATCH 07/15] Parallel loop for the statistics --- .../libnest2d/backends/clipper/geometries.hpp | 4 +- src/libslic3r/SLAPrint.cpp | 94 +++++++++++-------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index d0cc448964..15eeb57b8a 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -347,9 +347,7 @@ inline std::vector clipper_execute( auto traverse = [&processPoly] (ClipperLib::PolyNode *node) { - for(auto ch : node->Childs) { - processPoly(ch); - } + for(auto ch : node->Childs) processPoly(ch); }; traverse(&result); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 9e03b6f842..a5aa826c98 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -956,16 +956,15 @@ void SLAPrint::process() m_print_statistics.clear(); - using ClipperPolygon = libnest2d::PolygonImpl; - using ClipperPath = ClipperLib::Path; using ClipperPoint = ClipperLib::IntPoint; + using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d using ClipperPolygons = std::vector; - using libnest2d::Radians; - namespace sl = libnest2d::shapelike; + namespace sl = libnest2d::shapelike; // For algorithms // If the raster has vertical orientation, we will flip the coordinates bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait; + // Set up custom union and diff functions for clipper polygons auto polyunion = [] (const ClipperPolygons& subjects) { ClipperLib::Clipper clipper; @@ -1003,7 +1002,8 @@ void SLAPrint::process() return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); }; - auto area = [](const ClipperPolygon& poly) { return - sl::area(poly); }; + // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise + auto areafn = [](const ClipperPolygon& poly) { return - sl::area(poly); }; const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0; @@ -1051,7 +1051,7 @@ void SLAPrint::process() auto pfirst = hole.front(); hole.emplace_back(pfirst); } - sl::rotate(poly, Radians(double(instances[i].rotation))); + sl::rotate(poly, double(instances[i].rotation)); sl::translate(poly, ClipperPoint{instances[i].shift(X), instances[i].shift(Y)}); if (flpXY) { @@ -1070,10 +1070,10 @@ void SLAPrint::process() return polygons; }; - double supports_volume = 0.0; - double models_volume = 0.0; + double supports_volume(0.0); + double models_volume(0.0); - double estim_time = 0.0; + double estim_time(0.0); size_t slow_layers = 0; size_t fast_layers = 0; @@ -1081,16 +1081,22 @@ void SLAPrint::process() const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); double fade_layer_time = init_exp_time; - int sliced_layer_cnt = 0; - for (PrintLayer& layer : m_printer_input) - { - // vector of slice record references - auto& lyrslices = layer.slices(); + SpinMutex mutex; + using Lock = std::lock_guard; - if(lyrslices.empty()) continue; +// for (PrintLayer& layer : m_printer_input) + auto printlayerfn = [this, get_all_polygons, polyunion, polydiff, areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, + &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, &fast_layers, &fade_layer_time](size_t sliced_layer_cnt) + { + PrintLayer& layer = m_printer_input[sliced_layer_cnt]; + + // vector of slice record references + auto& slicerecord_references = layer.slices(); + + if(slicerecord_references.empty()) return; // Layer height should match for all object slices for a given level. - const auto l_height = double(lyrslices.front().get().layer_height()); + const auto l_height = double(slicerecord_references.front().get().layer_height()); // Calculation of the consumed material @@ -1128,23 +1134,25 @@ void SLAPrint::process() model_polygons = polyunion(model_polygons); double layer_model_area = 0; for (const ClipperPolygon& polygon : model_polygons) - layer_model_area += area(polygon); + layer_model_area += areafn(polygon); - if (layer_model_area < 0 || layer_model_area > 0) - models_volume += layer_model_area * l_height; + if (layer_model_area < 0 || layer_model_area > 0) { + Lock lck(mutex); models_volume += layer_model_area * l_height; + } if(!supports_polygons.empty()) { - if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); - else supports_polygons = polydiff(supports_polygons, model_polygons); + /*if(model_polygons.empty()) */supports_polygons = polyunion(supports_polygons); + /*else */ if(!model_polygons.empty())supports_polygons = polydiff(supports_polygons, model_polygons); // allegedly, union of subject is done withing the diff } double layer_support_area = 0; for (const ClipperPolygon& polygon : supports_polygons) - layer_support_area += area(polygon); + layer_support_area += areafn(polygon); - if (layer_support_area < 0 || layer_support_area > 0) - supports_volume += layer_support_area * l_height; + if (layer_support_area < 0 || layer_support_area > 0) { + Lock lck(mutex); supports_volume += layer_support_area * l_height; + } // Here we can save the expensively calculated polygons for printing ClipperPolygons trslices; @@ -1158,28 +1166,32 @@ void SLAPrint::process() const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; - if (is_fast_layer) - fast_layers++; - else - slow_layers++; + + { Lock lck(mutex); + if (is_fast_layer) + fast_layers++; + else + slow_layers++; - // Calculation of the printing time + // Calculation of the printing time - if (sliced_layer_cnt < 3) - estim_time += init_exp_time; - else if (fade_layer_time > exp_time) - { - fade_layer_time -= delta_fade_time; - estim_time += fade_layer_time; + if (sliced_layer_cnt < 3) + estim_time += init_exp_time; + else if (fade_layer_time > exp_time) + { + fade_layer_time -= delta_fade_time; + estim_time += fade_layer_time; + } + else + estim_time += exp_time; + + estim_time += tilt_time; } - else - estim_time += exp_time; + }; - estim_time += tilt_time; - - sliced_layer_cnt++; - } + for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); +// tbb::parallel_for(0, m_printer_input.size(), printlayerfn); m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR; m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR; From 19dc89bfab33fd242c105fa90a17f26ba2f4b5ec Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Thu, 28 Mar 2019 12:34:41 +0100 Subject: [PATCH 08/15] Clean up and fix TriangleMesh::split and relatives --- src/libslic3r/Model.cpp | 22 ++--- src/libslic3r/TriangleMesh.cpp | 142 +++++++++++++-------------------- src/libslic3r/TriangleMesh.hpp | 8 +- 3 files changed, 67 insertions(+), 105 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index f9db9fea0b..ffb80c5732 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -61,7 +61,7 @@ Model& Model::assign_copy(Model &&rhs) this->objects = std::move(rhs.objects); for (ModelObject *model_object : this->objects) model_object->set_model(this); - rhs.objects.clear(); + rhs.objects.clear(); return *this; } @@ -651,7 +651,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs) for (ModelInstance *model_instance : this->instances) model_instance->set_model_object(this); - return *this; + return *this; } void ModelObject::assign_new_unique_ids_recursive() @@ -970,8 +970,8 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) } } } - std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); }); - pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end()); + std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); }); + pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end()); Polygon hull; int n = (int)pts.size(); @@ -1291,11 +1291,11 @@ void ModelObject::split(ModelObjectPtrs* new_objects) // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? ModelObject* new_object = m_model->add_object(); - new_object->name = this->name; - new_object->config = this->config; - new_object->instances.reserve(this->instances.size()); - for (const ModelInstance *model_instance : this->instances) - new_object->add_instance(*model_instance); + new_object->name = this->name; + new_object->config = this->config; + new_object->instances.reserve(this->instances.size()); + for (const ModelInstance *model_instance : this->instances) + new_object->add_instance(*model_instance); ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh)); #if !ENABLE_VOLUMES_CENTERING_FIXES new_vol->center_geometry(); @@ -1467,9 +1467,9 @@ int ModelVolume::extruder_id() const bool ModelVolume::is_splittable() const { - // the call mesh.has_multiple_patches() is expensive, so cache the value to calculate it only once + // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once if (m_is_splittable == -1) - m_is_splittable = (int)mesh.has_multiple_patches(); + m_is_splittable = (int)mesh.is_splittable(); return m_is_splittable == 1; } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index eaa11b7381..467c44a8ab 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -338,113 +338,78 @@ void TriangleMesh::rotate(double angle, Point* center) this->translate(c(0), c(1), 0); } -bool TriangleMesh::has_multiple_patches() const +/** + * Calculates whether or not the mesh is splittable. + */ +bool TriangleMesh::is_splittable() const { - // we need neighbors - if (!this->repaired) - throw std::runtime_error("split() requires repair()"); - - if (this->stl.stats.number_of_facets == 0) - return false; + std::vector visited; + find_unvisited_neighbors(visited); - std::vector facet_queue(this->stl.stats.number_of_facets, 0); - std::vector facet_visited(this->stl.stats.number_of_facets, false); - int facet_queue_cnt = 1; - facet_queue[0] = 0; - facet_visited[0] = true; - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; - facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) { - int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; - if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } - } - - // If any of the face was not visited at the first time, return "multiple bodies". - for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) - if (! facet_visited[facet_idx]) - return true; - return false; + // Try finding an unvisited facet. If there are none, the mesh is not splittable. + auto it = std::find(visited.begin(), visited.end(), false); + return it != visited.end(); } -size_t TriangleMesh::number_of_patches() const +/** + * Visit all unvisited neighboring facets that are reachable from the first unvisited facet, + * and return them. + * + * @param facet_visited A reference to a vector of booleans. Contains whether or not a + * facet with the same index has been visited. + * @return A deque with all newly visited facets. + */ +std::deque TriangleMesh::find_unvisited_neighbors(std::vector &facet_visited) const { - // we need neighbors - if (!this->repaired) - throw std::runtime_error("split() requires repair()"); - - if (this->stl.stats.number_of_facets == 0) - return false; + // If the visited list is empty, populate it with false for every facet. + if (facet_visited.empty()) { + facet_visited = std::vector(this->stl.stats.number_of_facets, false); + } - std::vector facet_queue(this->stl.stats.number_of_facets, 0); - std::vector facet_visited(this->stl.stats.number_of_facets, false); - int facet_queue_cnt = 0; - size_t num_bodies = 0; - for (;;) { - // Find a seeding triangle for a new body. - int facet_idx = 0; - for (; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) - if (! facet_visited[facet_idx]) { - // A seed triangle was found. - facet_queue[facet_queue_cnt ++] = facet_idx; - facet_visited[facet_idx] = true; - break; - } - if (facet_idx == this->stl.stats.number_of_facets) - // No seed found. - break; - ++ num_bodies; - while (facet_queue_cnt > 0) { - int facet_idx = facet_queue[-- facet_queue_cnt]; + // Find the first unvisited facet. + std::queue facet_queue; + auto facet = std::find(facet_visited.begin(), facet_visited.end(), false); + if (facet != facet_visited.end()) + facet_queue.push(facet - facet_visited.begin()); + + // Traverse all reachable neighbors and mark them as visited. + std::deque facets; + while (!facet_queue.empty()) { + int facet_idx = facet_queue.front(); + facet_queue.pop(); + + if (facet_idx != -1 && !facet_visited[facet_idx]) { facet_visited[facet_idx] = true; - for (int j = 0; j < 3; ++ j) { - int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; - if (neighbor_idx != -1 && ! facet_visited[neighbor_idx]) - facet_queue[facet_queue_cnt ++] = neighbor_idx; - } + + facets.emplace_back(facet_idx); + for (int facet : this->stl.neighbors_start[facet_idx].neighbor) + facet_queue.push(facet); } } - return num_bodies; + return facets; } +/** + * Splits a mesh into multiple meshes when possible. + * + * @return A TriangleMeshPtrs with the newly created meshes. + */ TriangleMeshPtrs TriangleMesh::split() const { - TriangleMeshPtrs meshes; - std::vector facet_visited(this->stl.stats.number_of_facets, false); - - // we need neighbors + // Make sure we're not operating on a broken mesh. if (!this->repaired) throw std::runtime_error("split() requires repair()"); - // loop while we have remaining facets + // Loop while we have remaining facets. + std::vector facet_visited; + TriangleMeshPtrs meshes; for (;;) { - // get the first facet - std::queue facet_queue; - std::deque facets; - for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) { - if (! facet_visited[facet_idx]) { - // if facet was not seen put it into queue and start searching - facet_queue.push(facet_idx); - break; - } - } - if (facet_queue.empty()) + std::deque facets = find_unvisited_neighbors(facet_visited); + if (facets.empty()) break; - while (! facet_queue.empty()) { - int facet_idx = facet_queue.front(); - facet_queue.pop(); - if (! facet_visited[facet_idx]) { - facets.emplace_back(facet_idx); - for (int j = 0; j < 3; ++ j) - facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]); - facet_visited[facet_idx] = true; - } - } - + // Create a new mesh for the part that was just split off. TriangleMesh* mesh = new TriangleMesh; meshes.emplace_back(mesh); mesh->stl.stats.type = inmemory; @@ -453,8 +418,9 @@ TriangleMeshPtrs TriangleMesh::split() const stl_clear_error(&mesh->stl); stl_allocate(&mesh->stl); + // Assign the facets to the new mesh. bool first = true; - for (std::deque::const_iterator facet = facets.begin(); facet != facets.end(); ++ facet) { + for (auto facet = facets.begin(); facet != facets.end(); ++ facet) { mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet]; stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); } diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index a4387e5c1e..b204a9a3ec 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -68,12 +68,8 @@ public: size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } - // Returns true, if there are two and more connected patches in the mesh. - // Returns false, if one or zero connected patch is in the mesh. - bool has_multiple_patches() const; - - // Count disconnected triangle patches. - size_t number_of_patches() const; + bool is_splittable() const; + std::deque find_unvisited_neighbors(std::vector &facet_visited) const; stl_file stl; bool repaired; From 69199215b0be02799f40e4e24e3292f6dd62b43e Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Thu, 28 Mar 2019 12:37:10 +0100 Subject: [PATCH 09/15] Fix a bunch of warnings --- src/libslic3r/Model.cpp | 3 ++- src/libslic3r/TriangleMesh.cpp | 25 +++++++++++-------------- src/libslic3r/Utils.hpp | 4 ++-- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ffb80c5732..b5b9a008d0 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1609,6 +1609,7 @@ void ModelVolume::rotate(double angle, Axis axis) case X: { rotate(angle, Vec3d::UnitX()); break; } case Y: { rotate(angle, Vec3d::UnitY()); break; } case Z: { rotate(angle, Vec3d::UnitZ()); break; } + default: break; } } @@ -1625,6 +1626,7 @@ void ModelVolume::mirror(Axis axis) case X: { mirror(0) *= -1.0; break; } case Y: { mirror(1) *= -1.0; break; } case Z: { mirror(2) *= -1.0; break; } + default: break; } set_mirror(mirror); } @@ -1711,7 +1713,6 @@ bool model_object_list_extended(const Model &model_old, const Model &model_new) bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type) { - bool modifiers_differ = false; size_t i_old, i_new; for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) { const ModelVolume &mv_old = *model_object_old.volumes[i_old]; diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 467c44a8ab..9a1a9def4e 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -55,7 +55,7 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& f stl.stats.original_num_facets = stl.stats.number_of_facets; stl_allocate(&stl); - for (int i = 0; i < stl.stats.number_of_facets; i++) { + for (uint32_t i = 0; i < stl.stats.number_of_facets; i++) { stl_facet facet; facet.vertex[0] = points[facets[i](0)].cast(); facet.vertex[1] = points[facets[i](1)].cast(); @@ -125,9 +125,9 @@ void TriangleMesh::repair() float tolerance = stl.stats.shortest_edge; float increment = stl.stats.bounding_diameter / 10000.0; int iterations = 2; - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { for (int i = 0; i < iterations; i++) { - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations); #ifdef SLIC3R_TRACE_REPAIR BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby"; @@ -143,7 +143,7 @@ void TriangleMesh::repair() } // remove_unconnected - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { #ifdef SLIC3R_TRACE_REPAIR BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets"; #endif /* SLIC3R_TRACE_REPAIR */ @@ -212,9 +212,9 @@ void TriangleMesh::check_topology() float tolerance = stl.stats.shortest_edge; float increment = stl.stats.bounding_diameter / 10000.0; int iterations = 2; - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { for (int i = 0; i < iterations; i++) { - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) { //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations); stl_check_facets_nearby(&stl, tolerance); //printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed); @@ -442,7 +442,7 @@ void TriangleMesh::merge(const TriangleMesh &mesh) stl_reallocate(&this->stl); // copy facets - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i) + for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++ i) this->stl.facet_start[number_of_facets + i] = mesh.stl.facet_start[i]; // update size @@ -455,7 +455,7 @@ ExPolygons TriangleMesh::horizontal_projection() const { Polygons pp; pp.reserve(this->stl.stats.number_of_facets); - for (int i = 0; i < this->stl.stats.number_of_facets; ++ i) { + for (uint32_t i = 0; i < this->stl.stats.number_of_facets; ++ i) { stl_facet* facet = &this->stl.facet_start[i]; Polygon p; p.points.resize(3); @@ -497,7 +497,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c BoundingBoxf3 bbox; if (stl.v_shared == nullptr) { // Using the STL faces. - for (int i = 0; i < this->facets_count(); ++ i) { + for (size_t i = 0; i < this->facets_count(); ++ i) { const stl_facet &facet = this->stl.facet_start[i]; for (size_t j = 0; j < 3; ++ j) bbox.merge(trafo * facet.vertex[j].cast()); @@ -622,7 +622,7 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type }; std::vector edges_map; edges_map.assign(this->mesh->stl.stats.number_of_facets * 3, EdgeToFace()); - for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) + for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) for (int i = 0; i < 3; ++ i) { EdgeToFace &e2f = edges_map[facet_idx*3+i]; e2f.vertex_low = this->mesh->stl.v_indices[facet_idx].vertex[i]; @@ -871,7 +871,6 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal; // We may ignore this edge for slicing purposes, but we may still use it for object cutting. FacetSliceType result = Slicing; - const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx]; if (min_z == max_z) { // All three vertices are aligned with slice_z. line_out->edge_type = feHorizontal; @@ -883,8 +882,6 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet( } } else { // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane. - int nbr_idx = j % 3; - int nbr_face = nbr.neighbor[nbr_idx]; // Is the third vertex below the cutting plane? bool third_below = v0.z() < slice_z || v1.z() < slice_z || v2.z() < slice_z; // Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice @@ -1663,7 +1660,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object"; float scaled_z = scale_(z); - for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) { + for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) { stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; // find facet extents diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index c13df45462..dfd72b7a91 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -208,7 +208,7 @@ public: // Shorten the dhms time by removing the seconds, rounding the dhm to full minutes // and removing spaces. -static std::string short_time(const std::string &time) +inline std::string short_time(const std::string &time) { // Parse the dhms time format. int days = 0; @@ -247,7 +247,7 @@ static std::string short_time(const std::string &time) } // Returns the given time is seconds in format DDd HHh MMm SSs -static std::string get_time_dhms(float time_in_secs) +inline std::string get_time_dhms(float time_in_secs) { int days = (int)(time_in_secs / 86400.0f); time_in_secs -= (float)days * 86400.0f; From 4a9e05194c57e2fd2c00459828ba7509bc73b883 Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Thu, 28 Mar 2019 12:39:57 +0100 Subject: [PATCH 10/15] Move repaired check to find_unvisited_neighbors --- src/libslic3r/TriangleMesh.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 9a1a9def4e..7595688603 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -361,6 +361,10 @@ bool TriangleMesh::is_splittable() const */ std::deque TriangleMesh::find_unvisited_neighbors(std::vector &facet_visited) const { + // Make sure we're not operating on a broken mesh. + if (!this->repaired) + throw std::runtime_error("split() requires repair()"); + // If the visited list is empty, populate it with false for every facet. if (facet_visited.empty()) { facet_visited = std::vector(this->stl.stats.number_of_facets, false); @@ -397,10 +401,6 @@ std::deque TriangleMesh::find_unvisited_neighbors(std::vector &f */ TriangleMeshPtrs TriangleMesh::split() const { - // Make sure we're not operating on a broken mesh. - if (!this->repaired) - throw std::runtime_error("split() requires repair()"); - // Loop while we have remaining facets. std::vector facet_visited; TriangleMeshPtrs meshes; @@ -417,7 +417,7 @@ TriangleMeshPtrs TriangleMesh::split() const mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets; stl_clear_error(&mesh->stl); stl_allocate(&mesh->stl); - + // Assign the facets to the new mesh. bool first = true; for (auto facet = facets.begin(); facet != facets.end(); ++ facet) { @@ -425,7 +425,7 @@ TriangleMeshPtrs TriangleMesh::split() const stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first); } } - + return meshes; } From cd3cec3e45535ab42f7e36d50730299a91dada3e Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Fri, 29 Mar 2019 01:27:20 +0100 Subject: [PATCH 11/15] Use number_of_parts for is_splittable It's there, why not use it --- src/libslic3r/Model.cpp | 6 +----- src/libslic3r/Model.hpp | 6 ------ src/libslic3r/TriangleMesh.cpp | 13 ------------- src/libslic3r/TriangleMesh.hpp | 1 - 4 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index b5b9a008d0..a9835eaa19 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1467,11 +1467,7 @@ int ModelVolume::extruder_id() const bool ModelVolume::is_splittable() const { - // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once - if (m_is_splittable == -1) - m_is_splittable = (int)mesh.is_splittable(); - - return m_is_splittable == 1; + return mesh.stl.stats.number_of_parts > 1; } void ModelVolume::center_geometry() diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 8a48f8ee96..919e7715c0 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -420,12 +420,6 @@ private: TriangleMesh m_convex_hull; Geometry::Transformation m_transformation; - // flag to optimize the checking if the volume is splittable - // -1 -> is unknown value (before first cheking) - // 0 -> is not splittable - // 1 -> is splittable - mutable int m_is_splittable{ -1 }; - ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object) { if (mesh.stl.stats.number_of_facets > 1) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 7595688603..1baf3cee30 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -338,19 +338,6 @@ void TriangleMesh::rotate(double angle, Point* center) this->translate(c(0), c(1), 0); } -/** - * Calculates whether or not the mesh is splittable. - */ -bool TriangleMesh::is_splittable() const -{ - std::vector visited; - find_unvisited_neighbors(visited); - - // Try finding an unvisited facet. If there are none, the mesh is not splittable. - auto it = std::find(visited.begin(), visited.end(), false); - return it != visited.end(); -} - /** * Visit all unvisited neighboring facets that are reachable from the first unvisited facet, * and return them. diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index b204a9a3ec..e538b76937 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -68,7 +68,6 @@ public: size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } - bool is_splittable() const; std::deque find_unvisited_neighbors(std::vector &facet_visited) const; stl_file stl; From 2baa651f1e21b81a00bf23975fce711c5642e4ae Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 1 Apr 2019 12:15:47 +0200 Subject: [PATCH 12/15] Fixing the broken rasterizer. Paths and holes cannot be added separately. --- .../libnest2d/backends/clipper/geometries.hpp | 21 ++++++++--- src/libslic3r/Rasterizer/Rasterizer.cpp | 35 +++++++++++++++---- src/libslic3r/SLAPrint.cpp | 23 +++++++----- 3 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp index 15eeb57b8a..232668f610 100644 --- a/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp +++ b/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp @@ -331,8 +331,15 @@ inline std::vector clipper_execute( auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) { PolygonImpl poly; - poly.Contour.swap(pptr->Contour); auto front_p = poly.Contour.front(); - poly.Contour.emplace_back(std::move(front_p)); + poly.Contour.swap(pptr->Contour); + + assert(!pptr->IsHole()); + + if(pptr->IsOpen()) { + auto front_p = poly.Contour.front(); + poly.Contour.emplace_back(front_p); + } + for(auto h : pptr->Childs) { processHole(h, poly); } retv.push_back(poly); }; @@ -340,8 +347,14 @@ inline std::vector clipper_execute( processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly) { poly.Holes.emplace_back(std::move(pptr->Contour)); - auto front_p = poly.Holes.back().front(); - poly.Holes.back().emplace_back(std::move(front_p)); + + assert(pptr->IsHole()); + + if(pptr->IsOpen()) { + auto front_p = poly.Holes.back().front(); + poly.Holes.back().emplace_back(front_p); + } + for(auto c : pptr->Childs) processPoly(c); }; diff --git a/src/libslic3r/Rasterizer/Rasterizer.cpp b/src/libslic3r/Rasterizer/Rasterizer.cpp index ab584821fe..496a584a24 100644 --- a/src/libslic3r/Rasterizer/Rasterizer.cpp +++ b/src/libslic3r/Rasterizer/Rasterizer.cpp @@ -71,16 +71,41 @@ public: clear(); } - template inline void draw(const Geometry &poly) { + void draw(const ExPolygon &poly) { agg::rasterizer_scanline_aa<> ras; agg::scanline_p8 scanlines; - auto&& path = to_path(poly); + auto&& path = to_path(poly.contour); if(m_o == Origin::TOP_LEFT) flipy(path); ras.add_path(path); + for(auto h : poly.holes) { + auto&& holepath = to_path(h); + if(m_o == Origin::TOP_LEFT) flipy(holepath); + ras.add_path(holepath); + } + + agg::render_scanlines(ras, scanlines, m_renderer); + } + + void draw(const ClipperLib::Polygon &poly) { + agg::rasterizer_scanline_aa<> ras; + agg::scanline_p8 scanlines; + + auto&& path = to_path(poly.Contour); + + if(m_o == Origin::TOP_LEFT) flipy(path); + + ras.add_path(path); + + for(auto h : poly.Holes) { + auto&& holepath = to_path(h); + if(m_o == Origin::TOP_LEFT) flipy(holepath); + ras.add_path(holepath); + } + agg::render_scanlines(ras, scanlines, m_renderer); } @@ -186,14 +211,12 @@ void Raster::clear() void Raster::draw(const ExPolygon &expoly) { - m_impl->draw(expoly.contour); - for(auto& h : expoly.holes) m_impl->draw(h); + m_impl->draw(expoly); } void Raster::draw(const ClipperLib::Polygon &poly) { - m_impl->draw(poly.Contour); - for(auto& h : poly.Holes) m_impl->draw(h); + m_impl->draw(poly); } void Raster::save(std::ostream& stream, Compression comp) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index a5aa826c98..4248814914 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1084,9 +1084,15 @@ void SLAPrint::process() SpinMutex mutex; using Lock = std::lock_guard; -// for (PrintLayer& layer : m_printer_input) - auto printlayerfn = [this, get_all_polygons, polyunion, polydiff, areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, - &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, &fast_layers, &fade_layer_time](size_t sliced_layer_cnt) + // Going to parallel: + auto printlayerfn = [this, + // functions and read only vars + get_all_polygons, polyunion, polydiff, areafn, + area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, + + // write vars + &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, + &fast_layers, &fade_layer_time](size_t sliced_layer_cnt) { PrintLayer& layer = m_printer_input[sliced_layer_cnt]; @@ -1141,9 +1147,9 @@ void SLAPrint::process() } if(!supports_polygons.empty()) { - /*if(model_polygons.empty()) */supports_polygons = polyunion(supports_polygons); - /*else */ if(!model_polygons.empty())supports_polygons = polydiff(supports_polygons, model_polygons); - // allegedly, union of subject is done withing the diff + if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); + else supports_polygons = polydiff(supports_polygons, model_polygons); + // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType } double layer_support_area = 0; @@ -1190,8 +1196,9 @@ void SLAPrint::process() } }; - for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); -// tbb::parallel_for(0, m_printer_input.size(), printlayerfn); + // sequential version for debugging: + // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); + tbb::parallel_for(0, m_printer_input.size(), printlayerfn); m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR; m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR; From d728f4be5e9a4c78c85a8e0bc1880b4cdb025f9c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 1 Apr 2019 12:27:45 +0200 Subject: [PATCH 13/15] Revert "Use number_of_parts for is_splittable" It is too dangerous to rely on the admesh flag without inspecting the admesh code line by line and a through test. This reverts commit cd3cec3e45535ab42f7e36d50730299a91dada3e. --- src/libslic3r/Model.cpp | 6 +++++- src/libslic3r/Model.hpp | 6 ++++++ src/libslic3r/TriangleMesh.cpp | 13 +++++++++++++ src/libslic3r/TriangleMesh.hpp | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index a9835eaa19..b5b9a008d0 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1467,7 +1467,11 @@ int ModelVolume::extruder_id() const bool ModelVolume::is_splittable() const { - return mesh.stl.stats.number_of_parts > 1; + // the call mesh.is_splittable() is expensive, so cache the value to calculate it only once + if (m_is_splittable == -1) + m_is_splittable = (int)mesh.is_splittable(); + + return m_is_splittable == 1; } void ModelVolume::center_geometry() diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 919e7715c0..8a48f8ee96 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -420,6 +420,12 @@ private: TriangleMesh m_convex_hull; Geometry::Transformation m_transformation; + // flag to optimize the checking if the volume is splittable + // -1 -> is unknown value (before first cheking) + // 0 -> is not splittable + // 1 -> is splittable + mutable int m_is_splittable{ -1 }; + ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(ModelVolumeType::MODEL_PART), object(object) { if (mesh.stl.stats.number_of_facets > 1) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 1baf3cee30..7595688603 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -338,6 +338,19 @@ void TriangleMesh::rotate(double angle, Point* center) this->translate(c(0), c(1), 0); } +/** + * Calculates whether or not the mesh is splittable. + */ +bool TriangleMesh::is_splittable() const +{ + std::vector visited; + find_unvisited_neighbors(visited); + + // Try finding an unvisited facet. If there are none, the mesh is not splittable. + auto it = std::find(visited.begin(), visited.end(), false); + return it != visited.end(); +} + /** * Visit all unvisited neighboring facets that are reachable from the first unvisited facet, * and return them. diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index e538b76937..b204a9a3ec 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -68,6 +68,7 @@ public: size_t facets_count() const { return this->stl.stats.number_of_facets; } bool empty() const { return this->facets_count() == 0; } + bool is_splittable() const; std::deque find_unvisited_neighbors(std::vector &facet_visited) const; stl_file stl; From d4b22cfb87f8b6bc9c8c7ec34158be2e852450e8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 1 Apr 2019 13:53:48 +0200 Subject: [PATCH 14/15] Fix dragging of mixed instances plus volumes selections --- src/slic3r/GUI/Selection.cpp | 53 ++++++++++++++++++++++++++++++------ src/slic3r/GUI/Selection.hpp | 1 + 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 5d286846b4..34b5f4067b 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -118,17 +118,17 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection) if (needs_reset) clear(); - if (volume->is_modifier) - m_mode = Volume; - else if (!contains_volume(volume_idx)) - m_mode = Instance; - // else -> keep current mode + if (!contains_volume(volume_idx)) + m_mode = volume->is_modifier ? Volume : Instance; + else + // keep current mode + return; switch (m_mode) { case Volume: { - if (volume->volume_idx() >= 0 && (is_empty() || (volume->instance_idx() == get_instance_idx()))) + if ((volume->volume_idx() >= 0) && (is_empty() || (volume->instance_idx() == get_instance_idx()))) _add_volume(volume_idx); break; @@ -439,6 +439,8 @@ void Selection::translate(const Vec3d& displacement, bool local) if (!m_valid) return; + EMode translation_type = m_mode; + for (unsigned int i : m_list) { if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) @@ -452,13 +454,22 @@ void Selection::translate(const Vec3d& displacement, bool local) } } else if (m_mode == Instance) - (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); + { + if (_is_from_fully_selected_instance(i)) + (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); + else + { + Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + translation_type = Volume; + } + } } #if !DISABLE_INSTANCES_SYNCH - if (m_mode == Instance) + if (translation_type == Instance) _synchronize_unselected_instances(SYNC_ROTATION_NONE); - else if (m_mode == Volume) + else if (translation_type == Volume) _synchronize_unselected_volumes(); #endif // !DISABLE_INSTANCES_SYNCH @@ -1683,5 +1694,29 @@ void Selection::_ensure_on_bed() } } +bool Selection::_is_from_fully_selected_instance(unsigned int volume_idx) const +{ + struct SameInstance + { + int obj_idx; + int inst_idx; + GLVolumePtrs& volumes; + + SameInstance(int obj_idx, int inst_idx, GLVolumePtrs& volumes) : obj_idx(obj_idx), inst_idx(inst_idx), volumes(volumes) {} + bool operator () (unsigned int i) { return (volumes[i]->object_idx() == obj_idx) && (volumes[i]->instance_idx() == inst_idx); } + }; + + if ((unsigned int)m_volumes->size() <= volume_idx) + return false; + + GLVolume* volume = (*m_volumes)[volume_idx]; + int object_idx = volume->object_idx(); + if ((int)m_model->objects.size() <= object_idx) + return false; + + unsigned int count = (unsigned int)std::count_if(m_list.begin(), m_list.end(), SameInstance(object_idx, volume->instance_idx(), *m_volumes)); + return count == (unsigned int)m_model->objects[object_idx]->volumes.size(); +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 200f239c1f..4b2a72d7c0 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -297,6 +297,7 @@ private: void _synchronize_unselected_instances(SyncRotationType sync_rotation_type); void _synchronize_unselected_volumes(); void _ensure_on_bed(); + bool _is_from_fully_selected_instance(unsigned int volume_idx) const; }; } // namespace GUI From 32a49d1468344f43722330bcdcc9495a6e35af61 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 1 Apr 2019 13:59:39 +0200 Subject: [PATCH 15/15] Fixing negative status values in console output --- src/slic3r.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 5198eeaa32..958b663059 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -361,6 +361,14 @@ int CLI::run(int argc, char **argv) std::string outfile = m_config.opt_string("output"); Print fff_print; SLAPrint sla_print; + + sla_print.set_status_callback( + [](const PrintBase::SlicingStatus& s) + { + if(s.percent >= 0) // FIXME: is this sufficient? + printf("%3d%s %s\n", s.percent, "% =>", s.text.c_str()); + }); + PrintBase *print = (printer_technology == ptFFF) ? static_cast(&fff_print) : static_cast(&sla_print); if (! m_config.opt_bool("dont_arrange")) { //FIXME make the min_object_distance configurable.