From 0d83c7ee9f71492a237906178b99a774b9e641c6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 22 Mar 2019 17:05:41 +0100 Subject: [PATCH 01/14] Hypothetical support of different object layer heights. --- src/libslic3r/MTUtils.hpp | 23 +++++++++++++++++++++-- src/libslic3r/SLAPrint.cpp | 26 ++++++++++++++++++++------ src/libslic3r/SLAPrint.hpp | 2 +- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 1f6ca1a237..7e91ace328 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -56,8 +56,18 @@ public: } }; -template +/// An std compatible random access iterator which uses indices to the source +/// vector thus resistant to invalidation caused by relocations. It also "knows" +/// its container. No comparison is neccesary to the container "end()" iterator. +/// The template can be instantiated with a different value type than that of +/// the container's but the types must be compatible. E.g. a base class of the +/// contained objects is compatible. +/// +/// For a constant iterator, one can instantiate this template with a value +/// type preceded with 'const'. +template class IndexBasedIterator { static const size_t NONE = size_t(-1); @@ -110,6 +120,8 @@ public: operator difference_type() { return difference_type(m_idx); } + /// Tesing the end of the container... this is not possible with std + /// iterators. inline bool is_end() const { return m_idx >= m_index_ref.get().size();} inline Value & operator*() const { @@ -122,6 +134,7 @@ public: return &m_index_ref.get().operator[](m_idx); } + /// If both iterators point past the container, they are equal... inline bool operator ==(const IndexBasedIterator& other) { size_t e = m_index_ref.get().size(); return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e); @@ -148,17 +161,23 @@ public: } }; +/// A very simple range concept implementation with iterator-like objects. template class Range { It from, to; public: + + // The class is ready for range based for loops. It begin() const { return from; } It end() const { return to; } + + // The iterator type can be obtained this way. using Type = It; Range() = default; Range(It &&b, It &&e): from(std::forward(b)), to(std::forward(e)) {} + // Some useful container-like methods... inline size_t size() const { return end() - begin(); } inline bool empty() const { return size() == 0; } }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 79834908f2..c49ea417ce 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -623,11 +623,8 @@ void SLAPrint::process() // shortcut to initial layer height double ilhd = m_material_config.initial_layer_height.getFloat(); auto ilh = float(ilhd); - double lhd = m_objects.front()->m_config.layer_height.getFloat(); - float lh = float(lhd); auto ilhs = LevelID(ilhd / SCALING_FACTOR); - auto lhs = LevelID(lhd / SCALING_FACTOR); const size_t objcount = m_objects.size(); const unsigned min_objstatus = 0; // where the per object operations start @@ -648,11 +645,15 @@ void SLAPrint::process() // Slicing the model object. This method is oversimplified and needs to // be compared with the fff slicing algorithm for verification - auto slice_model = [this, ilhs, lhs, ilh, lh](SLAPrintObject& po) { + auto slice_model = [this, ilhs, ilh](SLAPrintObject& po) { TriangleMesh mesh = po.transformed_mesh(); // We need to prepare the slice index... + double lhd = m_objects.front()->m_config.layer_height.getFloat(); + float lh = float(lhd); + auto lhs = LevelID(lhd / SCALING_FACTOR); + auto&& bb3d = mesh.bounding_box(); double minZ = bb3d.min(Z) - po.get_elevation(); double maxZ = bb3d.max(Z); @@ -716,6 +717,12 @@ void SLAPrint::process() // into the backend cache. if (mo.sla_points_status != sla::PointsStatus::UserModified) { + // Hypotetical use of the slice index: + // auto bb = po.transformed_mesh().bounding_box(); + // auto range = po.get_slice_records(bb.min(Z)); + // std::vector heights; heights.reserve(range.size()); + // for(auto& record : range) heights.emplace_back(record.slice_level()); + // calculate heights of slices (slices are calculated already) const std::vector& heights = po.m_model_height_levels; @@ -896,15 +903,22 @@ void SLAPrint::process() }; // Rasterizing the model objects, and their supports - auto rasterize = [this, max_objstatus]() { + auto rasterize = [this, max_objstatus, ilhs]() { if(canceled()) return; // clear the rasterizer input m_printer_input.clear(); + auto eps = LevelID(EPSILON / SCALING_FACTOR); for(SLAPrintObject * o : m_objects) { - LevelID gndlvl = o->get_slice_index().front().key(); + LevelID gndlvl = o->get_slice_index().front().key() - ilhs; + for(auto& slicerecord : o->get_slice_index()) { + LevelID lvlid = slicerecord.key() - gndlvl; + + // Neat trick to round the layer levels to the grid. + lvlid = eps * (lvlid / eps); + auto& lyrs = m_printer_input[slicerecord.key() - gndlvl]; const ExPolygons& objslices = o->get_slices_from_record(slicerecord, soModel); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index fbe2821b21..a22696032a 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -158,7 +158,7 @@ private: // Search the slice index for a particular level in integer coordinates. // If no such layer is present, it will return m_slice_index.end() - // This behavior can be suppressed by the second parameter. If it is true + // This behavior can be suppressed by the second parameter. If it is false // the method will return the closest (non-equal) record SliceIndex::iterator search_slice_index(_SliceRecord::Key key, bool exact = false); SliceIndex::const_iterator search_slice_index(_SliceRecord::Key key, bool = false) const; From 5abf64e1d2b93a7badd4db65d5e2bc19d44838ab Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 25 Mar 2019 10:35:42 +0100 Subject: [PATCH 02/14] Add static assert instead of returning empty result. --- src/libslic3r/SLAPrint.hpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index a22696032a..8c7a0fa978 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -191,17 +191,19 @@ public: template Range get_slice_records(Key from, Key to = std::numeric_limits::max()) const { + static_assert (std::is_integral::value || + std::is_floating_point::value, + "Only floating point or integral types are allowed."); + SliceIndex::const_iterator it_from, it_to; + if(std::is_integral::value) { it_from = search_slice_index(SliceRecord::Key(from)); it_to = search_slice_index(SliceRecord::Key(to)); } else if(std::is_floating_point::value) { it_from = search_slice_index(float(from)); it_to = search_slice_index(float(to)); - } else return { - SliceRecordConstIterator(m_slice_index, _SliceRecord::NONE ), - SliceRecordConstIterator(m_slice_index, _SliceRecord::NONE ), - }; + } auto start = m_slice_index.begin(); From d73d9309f1f2e1a75a1becc51b3fa9fb57628f99 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 25 Mar 2019 19:02:05 +0100 Subject: [PATCH 03/14] Refactoring and commenting slice index solution. --- src/libslic3r/SLAPrint.cpp | 145 ++++++-------------- src/libslic3r/SLAPrint.hpp | 241 +++++++++++++++++---------------- src/slic3r/GUI/GLCanvas3D.cpp | 27 ++-- src/slic3r/GUI/GUI_Preview.cpp | 9 +- 4 files changed, 184 insertions(+), 238 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c49ea417ce..03dd6045c7 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -624,7 +624,7 @@ void SLAPrint::process() double ilhd = m_material_config.initial_layer_height.getFloat(); auto ilh = float(ilhd); - auto ilhs = LevelID(ilhd / SCALING_FACTOR); + auto ilhs = coord_t(ilhd / SCALING_FACTOR); const size_t objcount = m_objects.size(); const unsigned min_objstatus = 0; // where the per object operations start @@ -652,24 +652,27 @@ void SLAPrint::process() double lhd = m_objects.front()->m_config.layer_height.getFloat(); float lh = float(lhd); - auto lhs = LevelID(lhd / SCALING_FACTOR); + auto lhs = coord_t(lhd / SCALING_FACTOR); auto&& bb3d = mesh.bounding_box(); double minZ = bb3d.min(Z) - po.get_elevation(); double maxZ = bb3d.max(Z); - auto minZs = LevelID(minZ / SCALING_FACTOR); - auto maxZs = LevelID(maxZ / SCALING_FACTOR); + auto minZs = coord_t(minZ / SCALING_FACTOR); + auto maxZs = coord_t(maxZ / SCALING_FACTOR); po.m_slice_index.clear(); po.m_slice_index.reserve(size_t(maxZs - (minZs + ilhs) / lhs) + 1); po.m_slice_index.emplace_back(minZs + ilhs, float(minZ) + ilh / 2.f, ilh); - for(LevelID h = minZs + ilhs + lhs; h <= maxZs; h += lhs) { + for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) { po.m_slice_index.emplace_back(h, float(h*SCALING_FACTOR) - lh / 2.f, lh); } - auto slindex_it = po.search_slice_index(float(bb3d.min(Z))); + // Just get the first record that is form the model: + auto slindex_it = po.closest_slice_record( + po.m_slice_index, float(bb3d.min(Z)), + std::numeric_limits::infinity()); if(slindex_it == po.m_slice_index.end()) throw std::runtime_error(L("Slicing had to be stopped " @@ -908,33 +911,46 @@ void SLAPrint::process() // clear the rasterizer input m_printer_input.clear(); - auto eps = LevelID(EPSILON / SCALING_FACTOR); + + size_t mx = 0; + for(SLAPrintObject * o : m_objects) { + if(auto m = o->get_slice_index().size() > mx) mx = m; + } + + m_printer_input.reserve(mx); + + auto eps = coord_t(SCALED_EPSILON); for(SLAPrintObject * o : m_objects) { - LevelID gndlvl = o->get_slice_index().front().key() - ilhs; + coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; for(auto& slicerecord : o->get_slice_index()) { - LevelID lvlid = slicerecord.key() - gndlvl; + coord_t lvlid = slicerecord.print_level() - gndlvl; // Neat trick to round the layer levels to the grid. lvlid = eps * (lvlid / eps); - auto& lyrs = m_printer_input[slicerecord.key() - gndlvl]; + auto it = std::lower_bound(m_printer_input.begin(), + m_printer_input.end(), + LayerRefs(lvlid)); + + if(it == m_printer_input.end() || it->level != lvlid) + it = m_printer_input.insert(it, LayerRefs(lvlid)); + + auto& lyrs = *it; const ExPolygons& objslices = o->get_slices_from_record(slicerecord, soModel); const ExPolygons& supslices = o->get_slices_from_record(slicerecord, soSupport); if(!objslices.empty()) - lyrs.emplace_back(objslices, o->instances()); + lyrs.refs.emplace_back(objslices, o->instances()); if(!supslices.empty()) - lyrs.emplace_back(supslices, o->instances()); + lyrs.refs.emplace_back(supslices, o->instances()); } } // collect all the keys - std::vector keys; keys.reserve(m_printer_input.size()); - for(auto& e : m_printer_input) keys.emplace_back(e.first); // If the raster has vertical orientation, we will flip the coordinates bool flpXY = m_printer_config.display_orientation.getInt() == @@ -977,17 +993,17 @@ void SLAPrint::process() // procedure to process one height level. This will run in parallel auto lvlfn = - [this, &slck, &keys, &printer, slot, sd, ist, &pst, flpXY] + [this, &slck, &printer, slot, sd, ist, &pst, flpXY] (unsigned level_id) { if(canceled()) return; - LayerRefs& lrange = m_printer_input[keys[level_id]]; + LayerRefs& lrange = m_printer_input[level_id]; // Switch to the appropriate layer in the printer printer.begin_layer(level_id); - for(auto& lyrref : lrange) { // for all layers in the current level + for(auto& lyrref : lrange.refs) { // for all layers in the current level if(canceled()) break; const Layer& sl = lyrref.lref; // get the layer reference const LayerCopies& copies = lyrref.copies; @@ -1235,7 +1251,7 @@ void SLAPrint::fill_statistics() size_t max_layers_cnt = 0; size_t highest_obj_idx = 0; for (SLAPrintObject *&po : m_objects) { - const SLAPrintObject::SliceIndex& slice_index = po->get_slice_index(); + auto& slice_index = po->get_slice_index(); if (! slice_index.empty()) { float z = (-- slice_index.end())->slice_level(); size_t cnt = slice_index.size(); @@ -1249,7 +1265,7 @@ void SLAPrint::fill_statistics() } const SLAPrintObject * highest_obj = m_objects[highest_obj_idx]; - const SLAPrintObject::SliceIndex& highest_obj_slice_index = highest_obj->get_slice_index(); + 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; @@ -1257,7 +1273,7 @@ void SLAPrint::fill_statistics() int sliced_layer_cnt = 0; for (const auto& layer : highest_obj_slice_index) { - const double l_height = (layer.key() == highest_obj_slice_index.begin()->key()) ? init_layer_height : layer_height; + const double l_height = (layer.print_level() == highest_obj_slice_index.begin()->print_level()) ? init_layer_height : layer_height; // Calculation of the consumed material @@ -1266,13 +1282,12 @@ void SLAPrint::fill_statistics() for (SLAPrintObject * po : m_objects) { - const SLAPrintObject::_SliceRecord *record = nullptr; + const SliceRecord *record = nullptr; { - const SLAPrintObject::SliceIndex& index = po->get_slice_index(); - auto it = po->search_slice_index(layer.slice_level() - float(EPSILON)); - if (it == index.end() || it->slice_level() > layer.slice_level() + float(EPSILON)) + const SliceRecord& slr = po->closest_slice_to_slice_level(layer.slice_level(), float(EPSILON)); + if (!slr.is_valid()) continue; - record = &(*it); + record = &slr; } const ExPolygons &modelslices = po->get_slices_from_record(*record, soModel); @@ -1486,77 +1501,13 @@ const TriangleMesh EMPTY_MESH; const ExPolygons EMPTY_SLICE; } +const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f); + const std::vector& SLAPrintObject::get_support_points() const { return m_supportdata->support_points; } -SLAPrintObject::SliceIndex::iterator -SLAPrintObject::search_slice_index(float slice_level) -{ - _SliceRecord query(0, slice_level, 0); - auto it = std::lower_bound(m_slice_index.begin(), m_slice_index.end(), - query, - [](const _SliceRecord& r1, const _SliceRecord& r2) - { - return r1.slice_level() < r2.slice_level(); - }); - - return it; -} - -SLAPrintObject::SliceIndex::const_iterator -SLAPrintObject::search_slice_index(float slice_level) const -{ - _SliceRecord query(0, slice_level, 0); - auto it = std::lower_bound(m_slice_index.cbegin(), m_slice_index.cend(), - query, - [](const _SliceRecord& r1, const _SliceRecord& r2) - { - return r1.slice_level() < r2.slice_level(); - }); - - return it; -} - -SLAPrintObject::SliceIndex::iterator -SLAPrintObject::search_slice_index(SLAPrintObject::_SliceRecord::Key key, - bool exact) -{ - _SliceRecord query(key, 0.f, 0.f); - auto it = std::lower_bound(m_slice_index.begin(), m_slice_index.end(), - query, - [](const _SliceRecord& r1, const _SliceRecord& r2) - { - return r1.key() < r2.key(); - }); - - // Return valid iterator only if the keys really match - if(exact && it != m_slice_index.end() && it->key() != key) - it = m_slice_index.end(); - - return it; -} - -SLAPrintObject::SliceIndex::const_iterator -SLAPrintObject::search_slice_index(SLAPrintObject::_SliceRecord::Key key, - bool exact) const -{ - _SliceRecord query(key, 0.f, 0.f); - auto it = std::lower_bound(m_slice_index.cbegin(), m_slice_index.cend(), - query, - [](const _SliceRecord& r1, const _SliceRecord& r2) - { - return r1.key() < r2.key(); - }); - - // Return valid iterator only if the keys really match - if(exact && it != m_slice_index.end() && it->key() != key) - it = m_slice_index.end(); - - return it; -} - const std::vector &SLAPrintObject::get_support_slices() const { // assert(is_step_done(slaposSliceSupports)); @@ -1565,7 +1516,7 @@ const std::vector &SLAPrintObject::get_support_slices() const } const ExPolygons &SLAPrintObject::get_slices_from_record( - const _SliceRecord &rec, + const SliceRecord &rec, SliceOrigin o) const { size_t idx = o == soModel ? rec.get_model_slice_idx() : @@ -1579,15 +1530,7 @@ const ExPolygons &SLAPrintObject::get_slices_from_record( return idx >= v.size() ? EMPTY_SLICE : v[idx]; } -const ExPolygons &SLAPrintObject::get_slices_from_record( - SLAPrintObject::SliceRecordConstIterator it, SliceOrigin o) const -{ - if(it.is_end()) return EMPTY_SLICE; - return get_slices_from_record(*it, o); -} - -const std::vector& -SLAPrintObject::get_slice_index() const +const std::vector & SLAPrintObject::get_slice_index() const { // assert(is_step_done(slaposIndexSlices)); return m_slice_index; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 8c7a0fa978..42317aa2ed 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -34,10 +34,68 @@ using _SLAPrintObjectBase = // Layers according to quantized height levels. This will be consumed by // the printer (rasterizer) in the SLAPrint class. -using LevelID = long long; +// using coord_t = long long; enum SliceOrigin { soSupport, soModel }; +// The public Slice record structure. It corresponds to one printable layer. +// To get the sliced polygons, use SLAPrintObject::get_slices_from_record +class SliceRecord { +public: + // this will be the max limit of size_t + static const size_t NONE = size_t(-1); + + static const SliceRecord EMPTY; + +private: + coord_t m_print_z = 0; // Top of the layer + float m_slice_z = 0.f; // Exact level of the slice + float m_height = 0.f; // Height of the sliced layer + + size_t m_model_slices_idx = NONE; + size_t m_support_slices_idx = NONE; + +public: + + SliceRecord(coord_t key, float slicez, float height): + m_print_z(key), m_slice_z(slicez), m_height(height) {} + + // The key will be the integer height level of the top of the layer. + inline coord_t print_level() const { return m_print_z; } + + // Returns the exact floating point Z coordinate of the slice + inline float slice_level() const { return m_slice_z; } + + // Returns the current layer height + inline float layer_height() const { return m_height; } + + bool is_valid() const { return std::isnan(m_slice_z); } + + template inline T level() const { + static_assert(std::is_integral::value || + std::is_floating_point::value, + "Slice record level is only valid for numeric types!"); + if (std::is_integral::value) return T(print_level()); + else return T(slice_level()); + } + + template inline static SliceRecord create(T val) { + static_assert(std::is_integral::value || + std::is_floating_point::value, + "Slice record level is only valid for numeric types!"); + if (std::is_integral::value) return { coord_t(val), 0.f, 0.f }; + else return { 0, float(val), 0.f }; + } + + // Methods for setting the indices into the slice vectors. + void set_model_slice_idx(size_t id) { m_model_slices_idx = id; } + void set_support_slice_idx(size_t id) { m_support_slices_idx = id; } + + inline size_t get_model_slice_idx() const { return m_model_slices_idx; } + inline size_t get_support_slice_idx() const { return m_support_slices_idx; } +}; + + class SLAPrintObject : public _SLAPrintObjectBase { private: // Prevents erroneous use by other classes. @@ -93,145 +151,84 @@ public: // This method returns the support points of this SLAPrintObject. const std::vector& get_support_points() const; - // The public Slice record structure. It corresponds to one printable layer. - // To get the sliced polygons, use SLAPrintObject::get_slices_from_record - class SliceRecord { - public: - using Key = LevelID; - - private: - Key m_print_z = 0; // Top of the layer - float m_slice_z = 0.f; // Exact level of the slice - float m_height = 0.f; // Height of the sliced layer - - protected: - SliceRecord(Key key, float slicez, float height): - m_print_z(key), m_slice_z(slicez), m_height(height) {} - - public: - - // The key will be the integer height level of the top of the layer. - inline Key key() const { return m_print_z; } - - // Returns the exact floating point Z coordinate of the slice - inline float slice_level() const { return m_slice_z; } - - // Returns the current layer height - inline float layer_height() const { return m_height; } - }; - private: - // An index record referencing the slices - // (get_model_slices(), get_support_slices()) where the keys are the height - // levels of the model in scaled-clipper coordinates. The levels correspond - // to the z coordinate of the object coordinate system. - class _SliceRecord: public SliceRecord { - public: - static const size_t NONE = size_t(-1); // this will be the max limit of size_t - private: - size_t m_model_slices_idx = NONE; - size_t m_support_slices_idx = NONE; + // This is a template method for searching the slice index either by + // an integer key: print_level or a floating point key: slice_level. + // The eps parameter gives the max deviation in + or - direction. + // + // This method can be used in const or non-const contexts as well. + template + static auto closest_slice_record(Container& cont, T lvl, T eps) -> decltype (cont.begin()) + { + if(cont.empty()) return cont.end(); + if(cont.size() == 1 && std::abs(cont.front().template level() - lvl) > eps) + return cont.end(); - public: - _SliceRecord(Key key, float slicez, float height): - SliceRecord(key, slicez, height) {} + SliceRecord query = SliceRecord::create(lvl); - // Methods for setting the indices into the slice vectors. - void set_model_slice_idx(size_t id) { m_model_slices_idx = id; } - void set_support_slice_idx(size_t id) { m_support_slices_idx = id; } + auto it = std::lower_bound(cont.begin(), cont.end(), query, + [](const SliceRecord& r1, + const SliceRecord& r2) + { + return r1.level() < r2.level(); + }); - inline size_t get_model_slice_idx() const { return m_model_slices_idx; } - inline size_t get_support_slice_idx() const { return m_support_slices_idx; } - }; + T diff = std::abs(it->template level() - lvl); - // Slice index will be a plain vector sorted by the integer height levels - using SliceIndex = std::vector<_SliceRecord>; + if(it != cont.begin()) { + auto it_prev = std::prev(it); + T diff_prev = std::abs(it_prev->template level() - lvl); + if(diff_prev < diff) { diff = diff_prev; it = it_prev; } + } - // Retrieve the slice index which is readable only after slaposIndexSlices - // is done. - const SliceIndex& get_slice_index() const; + if(diff > eps) it = cont.end(); - // Search slice index for the closest slice to the given level - SliceIndex::iterator search_slice_index(float slice_level); - SliceIndex::const_iterator search_slice_index(float slice_level) const; - - // Search the slice index for a particular level in integer coordinates. - // If no such layer is present, it will return m_slice_index.end() - // This behavior can be suppressed by the second parameter. If it is false - // the method will return the closest (non-equal) record - SliceIndex::iterator search_slice_index(_SliceRecord::Key key, bool exact = false); - SliceIndex::const_iterator search_slice_index(_SliceRecord::Key key, bool = false) const; - - const std::vector& get_model_slices() const; - const std::vector& get_support_slices() const; + return it; + } public: - // Should work as a polymorphic bidirectional iterator to the slice records - using SliceRecordConstIterator = - IndexBasedIterator; - // ///////////////////////////////////////////////////////////////////////// // - // These two methods should be callable on the client side (e.g. UI thread) + // These methods should be callable on the client side (e.g. UI thread) // when the appropriate steps slaposObjectSlice and slaposSliceSupports // are ready. All the print objects are processed before slapsRasterize so // it is safe to call them during and/or after slapsRasterize. // // ///////////////////////////////////////////////////////////////////////// - // Get the slice records from a range of slice levels (inclusive). Floating - // point keys are the levels where the model was sliced with the mesh - // slicer. Integral keys are the keys of the slice records, which - // correspond to the top of each layer.. The end() method of the returned - // range points *after* the last valid element. This is for being - // consistent with std and makeing range based for loops work. use - // std::prev(range.end()) or --range.end() to get the last element. - template Range - get_slice_records(Key from, Key to = std::numeric_limits::max()) const + // Retrieve the slice index. + const std::vector& get_slice_index() const; + + const std::vector& get_model_slices() const; + const std::vector& get_support_slices() const; + + // Search slice index for the closest slice to given print_level. + // max_epsilon gives the allowable deviation of the returned slice record's + // level. + const SliceRecord& closest_slice_to_print_level( + coord_t print_level, coord_t max_epsilon = coord_t(SCALED_EPSILON)) const { - static_assert (std::is_integral::value || - std::is_floating_point::value, - "Only floating point or integral types are allowed."); - - SliceIndex::const_iterator it_from, it_to; - - if(std::is_integral::value) { - it_from = search_slice_index(SliceRecord::Key(from)); - it_to = search_slice_index(SliceRecord::Key(to)); - } else if(std::is_floating_point::value) { - it_from = search_slice_index(float(from)); - it_to = search_slice_index(float(to)); - } - - auto start = m_slice_index.begin(); - - size_t bidx = it_from == m_slice_index.end() ? _SliceRecord::NONE : - size_t(it_from - start); - - size_t eidx = it_to == m_slice_index.end() ? _SliceRecord::NONE : - size_t(it_to - start) + 1; - - return { - SliceRecordConstIterator(m_slice_index, bidx), - SliceRecordConstIterator(m_slice_index, eidx), - }; + auto it = closest_slice_record(m_slice_index, print_level, max_epsilon); + if (it == m_slice_index.end()) return SliceRecord::EMPTY; + return *it; } - // Get all the slice records as a range. - inline Range get_slice_records() const { - return { - SliceRecordConstIterator(m_slice_index, 0), - SliceRecordConstIterator(m_slice_index, m_slice_index.size()) - }; + // Search slice index for the closest slice to given slice_level. + // max_epsilon gives the allowable deviation of the returned slice record's + // level. + const SliceRecord& closest_slice_to_slice_level( + float slice_level, float max_epsilon = float(EPSILON)) const + { + auto it = closest_slice_record(m_slice_index, slice_level, max_epsilon); + if (it == m_slice_index.end()) return SliceRecord::EMPTY; + return *it; } - const ExPolygons& get_slices_from_record(SliceRecordConstIterator it, - SliceOrigin o) const; - - const ExPolygons& get_slices_from_record(const _SliceRecord& rec, - SliceOrigin o) const; + // Get the actual slice polygons using a valid slice record. + const ExPolygons& get_slices_from_record( + const SliceRecord& rec, SliceOrigin o) const; protected: // to be called from SLAPrint only. friend class SLAPrint; @@ -272,7 +269,7 @@ private: // Exact (float) height levels mapped to the slices. Each record contains // the index to the model and the support slice vectors. - std::vector<_SliceRecord> m_slice_index; + std::vector m_slice_index; std::vector m_model_height_levels; @@ -395,8 +392,14 @@ private: // One level may contain multiple slices from multiple objects and their // supports - using LayerRefs = std::vector; - std::map m_printer_input; + struct LayerRefs { + coord_t level; + std::vector refs; + bool operator<(const LayerRefs& other) const { return level < other.level; } + explicit LayerRefs(coord_t lvl) : level(lvl) {} + }; + + std::vector m_printer_input; // The printer itself SLAPrinterPtr m_printer; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3192e7861e..c6a90a8cc2 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5009,19 +5009,20 @@ void GLCanvas3D::_render_sla_slices() const instance_transforms.push_back({ to_3d(unscale(inst.shift), 0.), Geometry::rad2deg(inst.rotation) }); } - if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) && obj->is_step_done(slaposIndexSlices)) + 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()) { double initial_layer_height = print->material_config().initial_layer_height.value; - LevelID key_zero = obj->get_slice_records().begin()->key(); - LevelID key_low = LevelID((clip_min_z - initial_layer_height) / SCALING_FACTOR) + key_zero; - LevelID key_high = LevelID((clip_max_z - initial_layer_height) / SCALING_FACTOR) + key_zero; - auto slice_range = obj->get_slice_records(key_low - LevelID(SCALED_EPSILON), key_high - LevelID(SCALED_EPSILON)); - auto it_low = slice_range.begin(); - auto it_high = std::prev(slice_range.end()); + coord_t key_zero = obj->get_slice_index().front().print_level(); + coord_t key_low = coord_t((clip_min_z - initial_layer_height) / SCALING_FACTOR) + key_zero; + coord_t key_high = coord_t((clip_max_z - initial_layer_height) / SCALING_FACTOR) + key_zero; + + SliceRecord slice_low = obj->closest_slice_to_print_level(key_low, coord_t(SCALED_EPSILON)); + SliceRecord slice_high = obj->closest_slice_to_print_level(key_high, coord_t(SCALED_EPSILON)); - if (! it_low.is_end() && it_low->key() < key_low + LevelID(SCALED_EPSILON)) { - const ExPolygons& obj_bottom = obj->get_slices_from_record(it_low, soModel); - const ExPolygons& sup_bottom = obj->get_slices_from_record(it_low, soSupport); + if (! slice_low.is_valid()) { + const ExPolygons& obj_bottom = obj->get_slices_from_record(slice_low, soModel); + const ExPolygons& sup_bottom = obj->get_slices_from_record(slice_low, soSupport); // calculate model bottom cap if (bottom_obj_triangles.empty() && !obj_bottom.empty()) bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z, true); @@ -5030,9 +5031,9 @@ void GLCanvas3D::_render_sla_slices() const bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z, true); } - if (! it_high.is_end() && it_high->key() < key_high + LevelID(SCALED_EPSILON)) { - const ExPolygons& obj_top = obj->get_slices_from_record(it_high, soModel); - const ExPolygons& sup_top = obj->get_slices_from_record(it_high, soSupport); + if (! slice_high.is_valid()) { + const ExPolygons& obj_top = obj->get_slices_from_record(slice_high, soModel); + const ExPolygons& sup_top = obj->get_slices_from_record(slice_high, soSupport); // calculate model top cap if (top_obj_triangles.empty() && !obj_top.empty()) top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z, false); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 2361ff6d39..18324f2e8a 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -772,12 +772,11 @@ 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)) + if (obj->is_step_done(slaposIndexSlices) && !obj->get_slice_index().empty()) { - auto slicerecords = obj->get_slice_records(); - auto low_coord = slicerecords.begin()->key(); - for (auto& rec : slicerecords) - zs.emplace_back(initial_layer_height + (rec.key() - low_coord) * SCALING_FACTOR); + auto low_coord = obj->get_slice_index().front().print_level(); + for (auto& rec : obj->get_slice_index()) + zs.emplace_back(initial_layer_height + (rec.print_level() - low_coord) * SCALING_FACTOR); } sort_remove_duplicates(zs); From 57e28b53f24670e5867958ff95ea9e1ba0cd6d4e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 10:57:45 +0100 Subject: [PATCH 04/14] Further refactor and simplification of slice index and print data. --- src/libslic3r/SLAPrint.cpp | 85 ++++++++++++++------------ src/libslic3r/SLAPrint.hpp | 108 +++++++++++++++++----------------- src/slic3r/GUI/GLCanvas3D.cpp | 8 +-- 3 files changed, 105 insertions(+), 96 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 03dd6045c7..e4b45d9a4a 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -698,7 +698,7 @@ void SLAPrint::process() id < po.m_model_slices.size() && mit != po.m_slice_index.end(); id++) { - mit->set_model_slice_idx(id); ++mit; + mit->set_model_slice_idx(po, id); ++mit; } }; @@ -894,7 +894,7 @@ void SLAPrint::process() i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i) { - po.m_slice_index[i].set_support_slice_idx(i); + po.m_slice_index[i].set_support_slice_idx(po, i); } }; @@ -924,7 +924,7 @@ void SLAPrint::process() for(SLAPrintObject * o : m_objects) { coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; - for(auto& slicerecord : o->get_slice_index()) { + for(const SliceRecord& slicerecord : o->get_slice_index()) { coord_t lvlid = slicerecord.print_level() - gndlvl; // Neat trick to round the layer levels to the grid. @@ -932,21 +932,13 @@ void SLAPrint::process() auto it = std::lower_bound(m_printer_input.begin(), m_printer_input.end(), - LayerRefs(lvlid)); + PrintLayer(lvlid)); if(it == m_printer_input.end() || it->level != lvlid) - it = m_printer_input.insert(it, LayerRefs(lvlid)); + it = m_printer_input.insert(it, PrintLayer(lvlid)); - auto& lyrs = *it; - const ExPolygons& objslices = o->get_slices_from_record(slicerecord, soModel); - const ExPolygons& supslices = o->get_slices_from_record(slicerecord, soSupport); - - if(!objslices.empty()) - lyrs.refs.emplace_back(objslices, o->instances()); - - if(!supslices.empty()) - lyrs.refs.emplace_back(supslices, o->instances()); + it->slices.emplace_back(std::cref(slicerecord)); } } @@ -998,25 +990,40 @@ void SLAPrint::process() { if(canceled()) return; - LayerRefs& lrange = m_printer_input[level_id]; + PrintLayer& lrange = m_printer_input[level_id]; // Switch to the appropriate layer in the printer printer.begin_layer(level_id); - for(auto& lyrref : lrange.refs) { // for all layers in the current level + for(const SliceRecord& slrecord : lrange.slices) + { // for all layers in the current level + if(canceled()) break; - const Layer& sl = lyrref.lref; // get the layer reference - const LayerCopies& copies = lyrref.copies; + + // get the layer reference + const ExPolygons& objslice = slrecord.get_slice(soModel); + const ExPolygons& supslice = slrecord.get_slice(soModel); + const SLAPrintObject *po = slrecord.print_obj(); + assert(po != nullptr); // Draw all the polygons in the slice to the actual layer. - for(auto& cp : copies) { - for(ExPolygon slice : sl) { + for(const SLAPrintObject::Instance& tr : po->instances()) { + for(ExPolygon poly : objslice) { // The order is important here: // apply rotation before translation... - slice.rotate(double(cp.rotation)); - slice.translate(cp.shift(X), cp.shift(Y)); - if(flpXY) swapXY(slice); - printer.draw_polygon(slice, level_id); + poly.rotate(double(tr.rotation)); + poly.translate(tr.shift(X), tr.shift(Y)); + if(flpXY) swapXY(poly); + printer.draw_polygon(poly, level_id); + } + + for(ExPolygon poly : supslice) { + // The order is important here: + // apply rotation before translation... + poly.rotate(double(tr.rotation)); + poly.translate(tr.shift(X), tr.shift(Y)); + if(flpXY) swapXY(poly); + printer.draw_polygon(poly, level_id); } } } @@ -1026,11 +1033,13 @@ void SLAPrint::process() // Status indication guarded with the spinlock auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size()); - { std::lock_guard lck(slck); - if( st > pst) { - report_status(*this, int(st), PRINT_STEP_LABELS[slapsRasterize]); - pst = st; - } + { + std::lock_guard lck(slck); + if( st > pst) { + report_status(*this, int(st), + PRINT_STEP_LABELS[slapsRasterize]); + pst = st; + } } }; @@ -1290,11 +1299,11 @@ void SLAPrint::fill_statistics() record = &slr; } - const ExPolygons &modelslices = po->get_slices_from_record(*record, soModel); + const ExPolygons &modelslices = record->get_slice(soModel); if (!modelslices.empty()) append(model_polygons, get_all_polygons(modelslices, po->instances())); - const ExPolygons &supportslices = po->get_slices_from_record(*record, soSupport); + const ExPolygons &supportslices = record->get_slice(soSupport); if (!supportslices.empty()) append(supports_polygons, get_all_polygons(supportslices, po->instances())); } @@ -1515,15 +1524,15 @@ const std::vector &SLAPrintObject::get_support_slices() const return m_supportdata->support_slices; } -const ExPolygons &SLAPrintObject::get_slices_from_record( - const SliceRecord &rec, - SliceOrigin o) const +const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const { - size_t idx = o == soModel ? rec.get_model_slice_idx() : - rec.get_support_slice_idx(); + size_t idx = o == soModel ? m_model_slices_idx : + m_support_slices_idx; - const std::vector& v = o == soModel? get_model_slices() : - get_support_slices(); + if(m_po == nullptr) return EMPTY_SLICE; + + const std::vector& v = o == soModel? m_po->get_model_slices() : + m_po->get_support_slices(); if(idx >= v.size()) return EMPTY_SLICE; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 42317aa2ed..6947cc80d0 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -38,8 +38,9 @@ using _SLAPrintObjectBase = enum SliceOrigin { soSupport, soModel }; +class SLAPrintObject; + // The public Slice record structure. It corresponds to one printable layer. -// To get the sliced polygons, use SLAPrintObject::get_slices_from_record class SliceRecord { public: // this will be the max limit of size_t @@ -54,6 +55,7 @@ private: size_t m_model_slices_idx = NONE; size_t m_support_slices_idx = NONE; + const SLAPrintObject *m_po = nullptr; public: @@ -61,38 +63,28 @@ public: m_print_z(key), m_slice_z(slicez), m_height(height) {} // The key will be the integer height level of the top of the layer. - inline coord_t print_level() const { return m_print_z; } + coord_t print_level() const { return m_print_z; } // Returns the exact floating point Z coordinate of the slice - inline float slice_level() const { return m_slice_z; } + float slice_level() const { return m_slice_z; } // Returns the current layer height - inline float layer_height() const { return m_height; } + float layer_height() const { return m_height; } bool is_valid() const { return std::isnan(m_slice_z); } - template inline T level() const { - static_assert(std::is_integral::value || - std::is_floating_point::value, - "Slice record level is only valid for numeric types!"); - if (std::is_integral::value) return T(print_level()); - else return T(slice_level()); - } - - template inline static SliceRecord create(T val) { - static_assert(std::is_integral::value || - std::is_floating_point::value, - "Slice record level is only valid for numeric types!"); - if (std::is_integral::value) return { coord_t(val), 0.f, 0.f }; - else return { 0, float(val), 0.f }; - } + const SLAPrintObject* print_obj() const { return m_po; } // Methods for setting the indices into the slice vectors. - void set_model_slice_idx(size_t id) { m_model_slices_idx = id; } - void set_support_slice_idx(size_t id) { m_support_slices_idx = id; } + void set_model_slice_idx(const SLAPrintObject &po, size_t id) { + m_po = &po; m_model_slices_idx = id; + } - inline size_t get_model_slice_idx() const { return m_model_slices_idx; } - inline size_t get_support_slice_idx() const { return m_support_slices_idx; } + void set_support_slice_idx(const SLAPrintObject& po, size_t id) { + m_po = &po; m_support_slices_idx = id; + } + + const ExPolygons& get_slice(SliceOrigin o) const; }; @@ -153,6 +145,16 @@ public: private: + template inline static T level(const SliceRecord& sr) { + static_assert(std::is_arithmetic::value, "Arithmetic only!"); + return std::is_integral::value ? T(sr.print_level()) : T(sr.slice_level()); + } + + template inline static SliceRecord create_slice_record(T val) { + static_assert(std::is_arithmetic::value, "Arithmetic only!"); + return std::is_integral::value ? SliceRecord{ coord_t(val), 0.f, 0.f } : SliceRecord{ 0, float(val), 0.f }; + } + // This is a template method for searching the slice index either by // an integer key: print_level or a floating point key: slice_level. // The eps parameter gives the max deviation in + or - direction. @@ -162,23 +164,23 @@ private: static auto closest_slice_record(Container& cont, T lvl, T eps) -> decltype (cont.begin()) { if(cont.empty()) return cont.end(); - if(cont.size() == 1 && std::abs(cont.front().template level() - lvl) > eps) + if(cont.size() == 1 && std::abs(level(cont.front()) - lvl) > eps) return cont.end(); - SliceRecord query = SliceRecord::create(lvl); + SliceRecord query = create_slice_record(lvl); auto it = std::lower_bound(cont.begin(), cont.end(), query, [](const SliceRecord& r1, const SliceRecord& r2) { - return r1.level() < r2.level(); + return level(r1) < level(r2); }); - T diff = std::abs(it->template level() - lvl); + T diff = std::abs(level(*it) - lvl); if(it != cont.begin()) { auto it_prev = std::prev(it); - T diff_prev = std::abs(it_prev->template level() - lvl); + T diff_prev = std::abs(level(*it_prev) - lvl); if(diff_prev < diff) { diff = diff_prev; it = it_prev; } } @@ -226,9 +228,6 @@ public: return *it; } - // Get the actual slice polygons using a valid slice record. - const ExPolygons& get_slices_from_record( - const SliceRecord& rec, SliceOrigin o) const; protected: // to be called from SLAPrint only. friend class SLAPrint; @@ -327,6 +326,24 @@ private: // Prevents erroneous use by other classes. typedef PrintBaseWithState Inherited; 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 + struct PrintLayer { + coord_t level; + + // The collection of slice records for the current level. + std::vector> slices; + + explicit PrintLayer(coord_t lvl) : level(lvl) {} + + // for being sorted in their container (see m_printer_input) + bool operator<(const PrintLayer& other) const { + return level < other.level; + } + }; + SLAPrint(): m_stepmask(slapsCount, true) {} virtual ~SLAPrint() override { this->clear(); } @@ -360,6 +377,10 @@ public: std::string validate() const override; + // 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; } + private: using SLAPrinter = FilePrinter; using SLAPrinterPtr = std::unique_ptr; @@ -377,29 +398,8 @@ private: PrintObjects m_objects; std::vector m_stepmask; - // Definition of the print input map. It consists of the slices indexed - // with scaled (clipper) Z coordinates. Also contains the instance - // transformations in scaled and filtered version. This is enough for the - // rasterizer to be able to draw every layer in the right position - using Layer = ExPolygons; - using LayerCopies = std::vector; - struct LayerRef { - std::reference_wrapper lref; - std::reference_wrapper copies; - LayerRef(const Layer& lyr, const LayerCopies& cp) : - lref(std::cref(lyr)), copies(std::cref(cp)) {} - }; - - // One level may contain multiple slices from multiple objects and their - // supports - struct LayerRefs { - coord_t level; - std::vector refs; - bool operator<(const LayerRefs& other) const { return level < other.level; } - explicit LayerRefs(coord_t lvl) : level(lvl) {} - }; - - std::vector m_printer_input; + // Ready-made data for rasterization. + std::vector m_printer_input; // The printer itself SLAPrinterPtr m_printer; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c6a90a8cc2..4bda48bcd6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5021,8 +5021,8 @@ void GLCanvas3D::_render_sla_slices() const SliceRecord slice_high = obj->closest_slice_to_print_level(key_high, coord_t(SCALED_EPSILON)); if (! slice_low.is_valid()) { - const ExPolygons& obj_bottom = obj->get_slices_from_record(slice_low, soModel); - const ExPolygons& sup_bottom = obj->get_slices_from_record(slice_low, soSupport); + const ExPolygons& obj_bottom = slice_low.get_slice(soModel); + const ExPolygons& sup_bottom = slice_low.get_slice(soSupport); // calculate model bottom cap if (bottom_obj_triangles.empty() && !obj_bottom.empty()) bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z, true); @@ -5032,8 +5032,8 @@ void GLCanvas3D::_render_sla_slices() const } if (! slice_high.is_valid()) { - const ExPolygons& obj_top = obj->get_slices_from_record(slice_high, soModel); - const ExPolygons& sup_top = obj->get_slices_from_record(slice_high, soSupport); + const ExPolygons& obj_top = slice_high.get_slice(soModel); + const ExPolygons& sup_top = slice_high.get_slice(soSupport); // calculate model top cap if (top_obj_triangles.empty() && !obj_top.empty()) top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z, false); From bec9a518a3f189bca9579a99e7d12fc10f6bd1bc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 11:22:35 +0100 Subject: [PATCH 05/14] Move SliceRecord into SLAPrintObject --- src/libslic3r/SLAPrint.hpp | 105 ++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 974da2f2de..d52aa0e8ac 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -38,56 +38,6 @@ using _SLAPrintObjectBase = enum SliceOrigin { soSupport, soModel }; -class SLAPrintObject; - -// The public Slice record structure. It corresponds to one printable layer. -class SliceRecord { -public: - // this will be the max limit of size_t - static const size_t NONE = size_t(-1); - - static const SliceRecord EMPTY; - -private: - coord_t m_print_z = 0; // Top of the layer - float m_slice_z = 0.f; // Exact level of the slice - float m_height = 0.f; // Height of the sliced layer - - size_t m_model_slices_idx = NONE; - size_t m_support_slices_idx = NONE; - const SLAPrintObject *m_po = nullptr; - -public: - - SliceRecord(coord_t key, float slicez, float height): - m_print_z(key), m_slice_z(slicez), m_height(height) {} - - // The key will be the integer height level of the top of the layer. - coord_t print_level() const { return m_print_z; } - - // Returns the exact floating point Z coordinate of the slice - float slice_level() const { return m_slice_z; } - - // Returns the current layer height - float layer_height() const { return m_height; } - - bool is_valid() const { return std::isnan(m_slice_z); } - - const SLAPrintObject* print_obj() const { return m_po; } - - // Methods for setting the indices into the slice vectors. - void set_model_slice_idx(const SLAPrintObject &po, size_t id) { - m_po = &po; m_model_slices_idx = id; - } - - void set_support_slice_idx(const SLAPrintObject& po, size_t id) { - m_po = &po; m_support_slices_idx = id; - } - - const ExPolygons& get_slice(SliceOrigin o) const; -}; - - class SLAPrintObject : public _SLAPrintObjectBase { private: // Prevents erroneous use by other classes. @@ -143,6 +93,53 @@ public: // This method returns the support points of this SLAPrintObject. const std::vector& get_support_points() const; + // The public Slice record structure. It corresponds to one printable layer. + class SliceRecord { + public: + // this will be the max limit of size_t + static const size_t NONE = size_t(-1); + + static const SliceRecord EMPTY; + + private: + coord_t m_print_z = 0; // Top of the layer + float m_slice_z = 0.f; // Exact level of the slice + float m_height = 0.f; // Height of the sliced layer + + size_t m_model_slices_idx = NONE; + size_t m_support_slices_idx = NONE; + const SLAPrintObject *m_po = nullptr; + + public: + + SliceRecord(coord_t key, float slicez, float height): + m_print_z(key), m_slice_z(slicez), m_height(height) {} + + // The key will be the integer height level of the top of the layer. + coord_t print_level() const { return m_print_z; } + + // Returns the exact floating point Z coordinate of the slice + float slice_level() const { return m_slice_z; } + + // Returns the current layer height + float layer_height() const { return m_height; } + + bool is_valid() const { return std::isnan(m_slice_z); } + + const SLAPrintObject* print_obj() const { return m_po; } + + // Methods for setting the indices into the slice vectors. + void set_model_slice_idx(const SLAPrintObject &po, size_t id) { + m_po = &po; m_model_slices_idx = id; + } + + void set_support_slice_idx(const SLAPrintObject& po, size_t id) { + m_po = &po; m_support_slices_idx = id; + } + + const ExPolygons& get_slice(SliceOrigin o) const; + }; + private: template inline static T level(const SliceRecord& sr) { @@ -189,6 +186,9 @@ private: return it; } + const std::vector& get_model_slices() const; + const std::vector& get_support_slices() const; + public: // ///////////////////////////////////////////////////////////////////////// @@ -203,9 +203,6 @@ public: // Retrieve the slice index. const std::vector& get_slice_index() const; - const std::vector& get_model_slices() const; - const std::vector& get_support_slices() const; - // Search slice index for the closest slice to given print_level. // max_epsilon gives the allowable deviation of the returned slice record's // level. @@ -281,6 +278,8 @@ private: using PrintObjects = std::vector; +using SliceRecord = SLAPrintObject::SliceRecord; + class TriangleMesh; struct SLAPrintStatistics From 9946a1c9148292ec1ffe33700cde4587e1ef93a4 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 11:30:55 +0100 Subject: [PATCH 06/14] Fix for missing support polygons --- src/libslic3r/SLAPrint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index e4b45d9a4a..2553fae856 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1002,7 +1002,7 @@ void SLAPrint::process() // get the layer reference const ExPolygons& objslice = slrecord.get_slice(soModel); - const ExPolygons& supslice = slrecord.get_slice(soModel); + const ExPolygons& supslice = slrecord.get_slice(soSupport); const SLAPrintObject *po = slrecord.print_obj(); assert(po != nullptr); From 221a4836fd53baffd55636548223a88e7a62a31f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 14:06:40 +0100 Subject: [PATCH 07/14] Manual unification of print polygons. With clipper not with the agg rasterizer, because they can be reused for the statistics. --- src/libslic3r/SLAPrint.cpp | 128 ++++++++++++++++++++++++++++--------- src/libslic3r/SLAPrint.hpp | 23 +++++-- 2 files changed, 115 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 2553fae856..0a26818663 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -934,11 +934,11 @@ void SLAPrint::process() m_printer_input.end(), PrintLayer(lvlid)); - if(it == m_printer_input.end() || it->level != lvlid) + if(it == m_printer_input.end() || it->level() != lvlid) it = m_printer_input.insert(it, PrintLayer(lvlid)); - it->slices.emplace_back(std::cref(slicerecord)); + it->add(slicerecord); } } @@ -990,43 +990,53 @@ void SLAPrint::process() { if(canceled()) return; - PrintLayer& lrange = m_printer_input[level_id]; + PrintLayer& printlayer = m_printer_input[level_id]; // Switch to the appropriate layer in the printer printer.begin_layer(level_id); - for(const SliceRecord& slrecord : lrange.slices) - { // for all layers in the current level + auto orientation = flpXY? SLADisplayOrientation::sladoPortrait : + SLADisplayOrientation::sladoLandscape; - if(canceled()) break; + // Get the transformed and properly oriented slice + const ExPolygons& slice = printlayer.transformed_slice(orientation); - // get the layer reference - const ExPolygons& objslice = slrecord.get_slice(soModel); - const ExPolygons& supslice = slrecord.get_slice(soSupport); - const SLAPrintObject *po = slrecord.print_obj(); - assert(po != nullptr); + // Now draw all the polygons with the printer... + for(const ExPolygon& p : slice) printer.draw_polygon(p, level_id); - // Draw all the polygons in the slice to the actual layer. - for(const SLAPrintObject::Instance& tr : po->instances()) { - for(ExPolygon poly : objslice) { - // The order is important here: - // apply rotation before translation... - poly.rotate(double(tr.rotation)); - poly.translate(tr.shift(X), tr.shift(Y)); - if(flpXY) swapXY(poly); - printer.draw_polygon(poly, level_id); - } - for(ExPolygon poly : supslice) { - // The order is important here: - // apply rotation before translation... - poly.rotate(double(tr.rotation)); - poly.translate(tr.shift(X), tr.shift(Y)); - if(flpXY) swapXY(poly); - printer.draw_polygon(poly, level_id); - } - } - } +// for(const SliceRecord& slrecord : printlayer.m_slices) +// { // for all layers in the current level + +// if(canceled()) break; + +// // get the layer reference +// const ExPolygons& objslice = slrecord.get_slice(soModel); +// const ExPolygons& supslice = slrecord.get_slice(soSupport); +// const SLAPrintObject *po = slrecord.print_obj(); +// assert(po != nullptr); + +// // Draw all the polygons in the slice to the actual layer. +// for(const SLAPrintObject::Instance& tr : po->instances()) { +// for(ExPolygon poly : objslice) { +// // The order is important here: +// // apply rotation before translation... +// poly.rotate(double(tr.rotation)); +// poly.translate(tr.shift(X), tr.shift(Y)); +// if(flpXY) swapXY(poly); +// printer.draw_polygon(poly, level_id); +// } + +// for(ExPolygon poly : supslice) { +// // The order is important here: +// // apply rotation before translation... +// poly.rotate(double(tr.rotation)); +// poly.translate(tr.shift(X), tr.shift(Y)); +// if(flpXY) swapXY(poly); +// printer.draw_polygon(poly, level_id); +// } +// } +// } // Finish the layer for later saving it. printer.finish_layer(level_id); @@ -1662,4 +1672,60 @@ std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in) return final_path; } +const ExPolygons &SLAPrint::PrintLayer::transformed_slice(SLADisplayOrientation o) const +{ + if (! m_trcache.empty()) return m_trcache; + + size_t cap = 0; + for (const SliceRecord& sr : m_slices) { + if(sr.print_obj()) { + size_t insts = sr.print_obj()->instances().size(); + cap += insts * (sr.get_slice(soModel).size() + + sr.get_slice(soSupport).size()); + } + } + + Polygons allpolys; + allpolys.reserve(cap); + + for (const SliceRecord& sr : m_slices) { + const ExPolygons& objsl = sr.get_slice(soModel); + const ExPolygons& supsl = sr.get_slice(soSupport); + + if (! sr.print_obj()) continue; + + for(const SLAPrintObject::Instance& tr : sr.print_obj()->instances()) + { + Polygons polys; + size_t polyscap = 0; + for(const ExPolygon &p : objsl) polyscap += p.holes.size() + 1; + for(const ExPolygon &p : supsl) polyscap += p.holes.size() + 1; + polys.reserve(polyscap); + + for(const ExPolygon &p : objsl) { + polys.emplace_back(p.contour); + for(auto& h : p.holes) polys.emplace_back(h); + } + + for(const ExPolygon &p : supsl) { + polys.emplace_back(p.contour); + for(auto& h : p.holes) polys.emplace_back(h); + } + + for(Polygon& poly : polys) { + poly.rotate(double(tr.rotation)); + poly.translate(tr.shift(X), tr.shift(Y)); + if(o == SLADisplayOrientation::sladoPortrait) + for(auto& p : poly.points) std::swap(p(X), p(Y)); + + allpolys.emplace_back(std::move(poly)); + } + } + } + + m_trcache = union_ex(allpolys); + + return m_trcache; +} + } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index d52aa0e8ac..9fe6d4cc4c 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -329,18 +329,31 @@ 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 - struct PrintLayer { - coord_t level; + class PrintLayer { + coord_t m_level; // The collection of slice records for the current level. - std::vector> slices; + std::vector> m_slices; - explicit PrintLayer(coord_t lvl) : level(lvl) {} + // No need for concurrency handling with CachedObject (hopefully) + mutable ExPolygons m_trcache; + + 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 level < other.level; + return m_level < other.m_level; } + + void add(const SliceRecord& sr) { + m_trcache.clear(); m_slices.emplace_back(sr); + } + + coord_t level() const { return m_level; } + + const ExPolygons& transformed_slice(SLADisplayOrientation o) const; }; SLAPrint(): m_stepmask(slapsCount, true) {} From 1fbdf7bdaa15127763624e9391cfb873989d79e8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 25 Mar 2019 15:03:35 +0100 Subject: [PATCH 08/14] Gizmos' grabbers size made uniform as the mean value of the selection's bounding box sides --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 12 +++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 2 +- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 3 ++- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 11 +++-------- 5 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 1e12674ec8..a1e95cfc53 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -231,13 +231,7 @@ std::array GLGizmoBase::picking_color_component(unsigned int id) const void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const { - float size = (float)box.max_size(); - - for (int i = 0; i < (int)m_grabbers.size(); ++i) - { - if (m_grabbers[i].enabled) - m_grabbers[i].render((m_hover_id == i), size); - } + render_grabbers((float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); } void GLGizmoBase::render_grabbers(float size) const @@ -251,7 +245,7 @@ void GLGizmoBase::render_grabbers(float size) const void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const { - float size = (float)box.max_size(); + float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i) { @@ -261,7 +255,7 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const m_grabbers[i].color[0] = color[0]; m_grabbers[i].color[1] = color[1]; m_grabbers[i].color[2] = color[2]; - m_grabbers[i].render_for_picking(size); + m_grabbers[i].render_for_picking(mean_size); } } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e228706c05..5660905040 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -173,7 +173,7 @@ void GLGizmoCut::on_render(const Selection& selection) const ::glEnd(); std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color); - m_grabbers[0].render(m_hover_id == 0, box.max_size()); + m_grabbers[0].render(m_hover_id == 0, (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); } void GLGizmoCut::on_render_for_picking(const Selection& selection) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 03151c4336..61f219dbff 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -216,7 +216,8 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box if (m_quadric == nullptr) return; - double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size((float)box.max_size()) : (double)m_grabbers[axis].get_half_size((float)box.max_size()); + float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); + double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size); float color[3]; ::memcpy((void*)color, (const void*)m_grabbers[axis].color, 3 * sizeof(float)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index 72b9ad18ac..e05ad00fe0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -307,7 +307,8 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick if (m_quadric == nullptr) return; - double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size((float)box.max_size()) : (double)m_grabbers[0].get_half_size((float)box.max_size()); + float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0); + double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size); float color[3]; ::memcpy((void*)color, (const void*)m_grabbers[0].color, 3 * sizeof(float)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 43152b56c6..e9f7e2ae2d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -116,8 +116,6 @@ void GLGizmoScale3D::on_render(const Selection& selection) const Vec3d angles = Vec3d::Zero(); Transform3d offsets_transform = Transform3d::Identity(); - Vec3d grabber_size = Vec3d::Zero(); - if (single_instance) { // calculate bounding box in instance local reference system @@ -135,7 +133,6 @@ void GLGizmoScale3D::on_render(const Selection& selection) const angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); - grabber_size = v->get_instance_transformation().get_matrix(true, true, false, true) * box.size(); } else if (single_volume) { @@ -145,13 +142,9 @@ void GLGizmoScale3D::on_render(const Selection& selection) const angles = Geometry::extract_euler_angles(transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); - grabber_size = v->get_volume_transformation().get_matrix(true, true, false, true) * box.size(); } else - { box = selection.get_bounding_box(); - grabber_size = box.size(); - } m_box = box; @@ -196,7 +189,9 @@ void GLGizmoScale3D::on_render(const Selection& selection) const ::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f); - float grabber_mean_size = (float)(grabber_size(0) + grabber_size(1) + grabber_size(2)) / 3.0f; + const BoundingBoxf3& selection_box = selection.get_bounding_box(); + + float grabber_mean_size = (float)((selection_box.size()(0) + selection_box.size()(1) + selection_box.size()(2)) / 3.0); if (m_hover_id == -1) { From 1c6b25c05316e2d5dd67073692036b0c876f1607 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 15:05:57 +0100 Subject: [PATCH 09/14] Optimizing manual unification. --- src/libslic3r/ClipperUtils.hpp | 6 ++-- src/libslic3r/SLAPrint.cpp | 65 +++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index a5ab4e99b0..0e58d7fac6 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -28,8 +28,8 @@ namespace Slic3r { //----------------------------------------------------------- // legacy code from Clipper documentation -void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons); -void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); +void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons *expolygons); +Slic3r::ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree); //----------------------------------------------------------- ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); @@ -228,4 +228,4 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons); } -#endif \ No newline at end of file +#endif diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 0a26818663..c68ee09f27 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1676,17 +1676,26 @@ const ExPolygons &SLAPrint::PrintLayer::transformed_slice(SLADisplayOrientation { if (! m_trcache.empty()) return m_trcache; - size_t cap = 0; - for (const SliceRecord& sr : m_slices) { - if(sr.print_obj()) { - size_t insts = sr.print_obj()->instances().size(); - cap += insts * (sr.get_slice(soModel).size() + - sr.get_slice(soSupport).size()); - } - } + ClipperLib::Clipper clipper; - Polygons allpolys; - allpolys.reserve(cap); + bool valid = true; + bool closed = true; + + auto rotate = [](ClipperLib::Path& path, double rads) { + + auto cosa = std::cos(rads); + auto sina = std::sin(rads); + + for(auto& p : path) { + p.X = ClipperLib::cInt(p.X * cosa - p.Y * sina); + p.Y = ClipperLib::cInt(p.X * sina + p.Y * cosa); + } + }; + + auto translate = [](ClipperLib::Path& path, coord_t dx, coord_t dy) + { + for(auto& p : path) { p.X += dx; p.Y += dy; } + }; for (const SliceRecord& sr : m_slices) { const ExPolygons& objsl = sr.get_slice(soModel); @@ -1696,34 +1705,40 @@ const ExPolygons &SLAPrint::PrintLayer::transformed_slice(SLADisplayOrientation for(const SLAPrintObject::Instance& tr : sr.print_obj()->instances()) { - Polygons polys; + ClipperLib::Paths polys; size_t polyscap = 0; for(const ExPolygon &p : objsl) polyscap += p.holes.size() + 1; for(const ExPolygon &p : supsl) polyscap += p.holes.size() + 1; polys.reserve(polyscap); - for(const ExPolygon &p : objsl) { - polys.emplace_back(p.contour); - for(auto& h : p.holes) polys.emplace_back(h); - } + auto cpyfn = [&polys](const ExPolygon &p) { + polys.emplace_back(Slic3rMultiPoint_to_ClipperPath(p.contour)); + for(auto& h : p.holes) + polys.emplace_back(Slic3rMultiPoint_to_ClipperPath(h)); + }; - for(const ExPolygon &p : supsl) { - polys.emplace_back(p.contour); - for(auto& h : p.holes) polys.emplace_back(h); - } + for(const ExPolygon &p : objsl) cpyfn(p); + for(const ExPolygon &p : supsl) cpyfn(p); + + for(ClipperLib::Path& poly : polys) { + rotate(poly, double(tr.rotation)); + translate(poly, tr.shift(X), tr.shift(Y)); - for(Polygon& poly : polys) { - poly.rotate(double(tr.rotation)); - poly.translate(tr.shift(X), tr.shift(Y)); if(o == SLADisplayOrientation::sladoPortrait) - for(auto& p : poly.points) std::swap(p(X), p(Y)); + for(ClipperLib::IntPoint& p : poly) std::swap(p.X, p.Y); - allpolys.emplace_back(std::move(poly)); + if(!poly.empty()) + valid &= clipper.AddPath(poly, ClipperLib::ptSubject, closed); } } } - m_trcache = union_ex(allpolys); + if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!"; + + ClipperLib::PolyTree result; + clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero); + + m_trcache = PolyTreeToExPolygons(result); return m_trcache; } From 6a41e45eeb0bdefa5665e538c0e7578a16de8f6e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 16:02:39 +0100 Subject: [PATCH 10/14] Reverting manual unification. It does not help performance. --- src/libslic3r/SLAPrint.cpp | 137 +++++++------------------------------ src/libslic3r/SLAPrint.hpp | 9 +-- 2 files changed, 25 insertions(+), 121 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c68ee09f27..d19a71968b 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -995,48 +995,28 @@ void SLAPrint::process() // Switch to the appropriate layer in the printer printer.begin_layer(level_id); - auto orientation = flpXY? SLADisplayOrientation::sladoPortrait : - SLADisplayOrientation::sladoLandscape; + using Instance = SLAPrintObject::Instance; - // Get the transformed and properly oriented slice - const ExPolygons& slice = printlayer.transformed_slice(orientation); + 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); + printer.draw_polygon(poly, level_id); + }; - // Now draw all the polygons with the printer... - for(const ExPolygon& p : slice) printer.draw_polygon(p, 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); -// for(const SliceRecord& slrecord : printlayer.m_slices) -// { // for all layers in the current level - -// if(canceled()) break; - -// // get the layer reference -// const ExPolygons& objslice = slrecord.get_slice(soModel); -// const ExPolygons& supslice = slrecord.get_slice(soSupport); -// const SLAPrintObject *po = slrecord.print_obj(); -// assert(po != nullptr); - -// // Draw all the polygons in the slice to the actual layer. -// for(const SLAPrintObject::Instance& tr : po->instances()) { -// for(ExPolygon poly : objslice) { -// // The order is important here: -// // apply rotation before translation... -// poly.rotate(double(tr.rotation)); -// poly.translate(tr.shift(X), tr.shift(Y)); -// if(flpXY) swapXY(poly); -// printer.draw_polygon(poly, level_id); -// } - -// for(ExPolygon poly : supslice) { -// // The order is important here: -// // apply rotation before translation... -// poly.rotate(double(tr.rotation)); -// poly.translate(tr.shift(X), tr.shift(Y)); -// if(flpXY) swapXY(poly); -// printer.draw_polygon(poly, level_id); -// } -// } -// } + 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); @@ -1248,7 +1228,7 @@ void SLAPrint::fill_statistics() for (size_t i = 0; i < inst_cnt; ++i) { ExPolygon tmp = polygon; - tmp.rotate(Geometry::rad2deg(instances[i].rotation)); + tmp.rotate(double(instances[i].rotation)); tmp.translate(instances[i].shift.x(), instances[i].shift.y()); polygons_append(polygons, to_polygons(std::move(tmp))); } @@ -1266,18 +1246,18 @@ void SLAPrint::fill_statistics() // 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.; + // 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(); + // 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; + // max_z = z; highest_obj_idx = &po - &m_objects.front(); } } @@ -1290,7 +1270,7 @@ void SLAPrint::fill_statistics() double fade_layer_time = init_exp_time; int sliced_layer_cnt = 0; - for (const auto& layer : highest_obj_slice_index) + for (const SliceRecord& layer : highest_obj_slice_index) { const double l_height = (layer.print_level() == highest_obj_slice_index.begin()->print_level()) ? init_layer_height : layer_height; @@ -1672,75 +1652,4 @@ std::string SLAPrintStatistics::finalize_output_path(const std::string &path_in) return final_path; } -const ExPolygons &SLAPrint::PrintLayer::transformed_slice(SLADisplayOrientation o) const -{ - if (! m_trcache.empty()) return m_trcache; - - ClipperLib::Clipper clipper; - - bool valid = true; - bool closed = true; - - auto rotate = [](ClipperLib::Path& path, double rads) { - - auto cosa = std::cos(rads); - auto sina = std::sin(rads); - - for(auto& p : path) { - p.X = ClipperLib::cInt(p.X * cosa - p.Y * sina); - p.Y = ClipperLib::cInt(p.X * sina + p.Y * cosa); - } - }; - - auto translate = [](ClipperLib::Path& path, coord_t dx, coord_t dy) - { - for(auto& p : path) { p.X += dx; p.Y += dy; } - }; - - for (const SliceRecord& sr : m_slices) { - const ExPolygons& objsl = sr.get_slice(soModel); - const ExPolygons& supsl = sr.get_slice(soSupport); - - if (! sr.print_obj()) continue; - - for(const SLAPrintObject::Instance& tr : sr.print_obj()->instances()) - { - ClipperLib::Paths polys; - size_t polyscap = 0; - for(const ExPolygon &p : objsl) polyscap += p.holes.size() + 1; - for(const ExPolygon &p : supsl) polyscap += p.holes.size() + 1; - polys.reserve(polyscap); - - auto cpyfn = [&polys](const ExPolygon &p) { - polys.emplace_back(Slic3rMultiPoint_to_ClipperPath(p.contour)); - for(auto& h : p.holes) - polys.emplace_back(Slic3rMultiPoint_to_ClipperPath(h)); - }; - - for(const ExPolygon &p : objsl) cpyfn(p); - for(const ExPolygon &p : supsl) cpyfn(p); - - for(ClipperLib::Path& poly : polys) { - rotate(poly, double(tr.rotation)); - translate(poly, tr.shift(X), tr.shift(Y)); - - if(o == SLADisplayOrientation::sladoPortrait) - for(ClipperLib::IntPoint& p : poly) std::swap(p.X, p.Y); - - if(!poly.empty()) - valid &= clipper.AddPath(poly, ClipperLib::ptSubject, closed); - } - } - } - - if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!"; - - ClipperLib::PolyTree result; - clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero); - - m_trcache = PolyTreeToExPolygons(result); - - return m_trcache; -} - } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 9fe6d4cc4c..626c5ead74 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -335,9 +335,6 @@ public: // The collection of slice records for the current level. std::vector> m_slices; - // No need for concurrency handling with CachedObject (hopefully) - mutable ExPolygons m_trcache; - public: explicit PrintLayer(coord_t lvl) : m_level(lvl) {} @@ -347,13 +344,11 @@ public: return m_level < other.m_level; } - void add(const SliceRecord& sr) { - m_trcache.clear(); m_slices.emplace_back(sr); - } + void add(const SliceRecord& sr) { m_slices.emplace_back(sr); } coord_t level() const { return m_level; } - const ExPolygons& transformed_slice(SLADisplayOrientation o) const; + auto slices() const -> const decltype (m_slices)& { return m_slices; } }; SLAPrint(): m_stepmask(slapsCount, true) {} From dc30242cad1374d676a075075c4026236a2cb991 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 26 Mar 2019 16:38:13 +0100 Subject: [PATCH 11/14] doc: Building on Windows: Add more detail --- doc/How to build - Windows.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/How to build - Windows.md b/doc/How to build - Windows.md index dec85ae483..5de983dd77 100644 --- a/doc/How to build - Windows.md +++ b/doc/How to build - Windows.md @@ -92,7 +92,7 @@ The `DESTDIR` option is the location where the bundle will be installed. This may be customized. If you leave it empty, the `DESTDIR` will be placed inside the same `build` directory. Warning: If the `build` directory is nested too deep inside other folders, various file paths during the build -become too long and the build might fail due to file writing errors. For this reason, it is recommended to +become too long and the build might fail due to file writing errors (\*). For this reason, it is recommended to place the `build` directory relatively close to the drive root. Note that the build variant that you may choose using Visual Studio (i.e. _Release_ or _Debug_ etc.) when building the dependency package is **not relevant**. @@ -100,3 +100,6 @@ The dependency build will by default build _both_ the _Release_ and _Debug_ vari You can disable building of the debug variant by passing the `-DDEP_DEBUG=OFF` option to CMake, this will only produce a _Release_ build. Refer to the CMake scripts inside the `deps` directory to see which dependencies are built in what versions and how this is done. + +\*) Specifically, the problem arises when building boost. Boost build tool appends all build options into paths of +intermediate files, which are not handled correctly by either `b2.exe` or possibly `ninja` (?). From 4d8800bc8a6e970f6679b07c7d30cccab051455c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 16:45:04 +0100 Subject: [PATCH 12/14] Fixing broken statistics. --- src/libslic3r/SLAPrint.cpp | 12 +++++------- src/libslic3r/SLAPrint.hpp | 21 ++++++++++++--------- src/slic3r/GUI/GLCanvas3D.cpp | 4 ++-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d19a71968b..019c91c06f 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -670,9 +670,8 @@ void SLAPrint::process() } // Just get the first record that is form the model: - auto slindex_it = po.closest_slice_record( - po.m_slice_index, float(bb3d.min(Z)), - std::numeric_limits::infinity()); + auto slindex_it = + po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); if(slindex_it == po.m_slice_index.end()) throw std::runtime_error(L("Slicing had to be stopped " @@ -1272,7 +1271,7 @@ void SLAPrint::fill_statistics() int sliced_layer_cnt = 0; for (const SliceRecord& layer : highest_obj_slice_index) { - const double l_height = (layer.print_level() == highest_obj_slice_index.begin()->print_level()) ? init_layer_height : layer_height; + const auto l_height = double(layer.layer_height()); // Calculation of the consumed material @@ -1283,9 +1282,8 @@ void SLAPrint::fill_statistics() { const SliceRecord *record = nullptr; { - const SliceRecord& slr = po->closest_slice_to_slice_level(layer.slice_level(), float(EPSILON)); - if (!slr.is_valid()) - continue; + const SliceRecord& slr = po->closest_slice_to_slice_level(layer.slice_level()); + if (!slr.is_valid()) continue; record = &slr; } diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 626c5ead74..88152830fe 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -124,7 +124,7 @@ public: // Returns the current layer height float layer_height() const { return m_height; } - bool is_valid() const { return std::isnan(m_slice_z); } + bool is_valid() const { return ! std::isnan(m_slice_z); } const SLAPrintObject* print_obj() const { return m_po; } @@ -158,7 +158,10 @@ private: // // This method can be used in const or non-const contexts as well. template - static auto closest_slice_record(Container& cont, T lvl, T eps) -> decltype (cont.begin()) + static auto closest_slice_record( + Container& cont, + T lvl, + T eps = std::numeric_limits::max()) -> decltype (cont.begin()) { if(cont.empty()) return cont.end(); if(cont.size() == 1 && std::abs(level(cont.front()) - lvl) > eps) @@ -207,22 +210,22 @@ public: // max_epsilon gives the allowable deviation of the returned slice record's // level. const SliceRecord& closest_slice_to_print_level( - coord_t print_level, coord_t max_epsilon = coord_t(SCALED_EPSILON)) const + coord_t print_level, + coord_t max_epsilon = std::numeric_limits::max()) const { auto it = closest_slice_record(m_slice_index, print_level, max_epsilon); - if (it == m_slice_index.end()) return SliceRecord::EMPTY; - return *it; + return it == m_slice_index.end() ? SliceRecord::EMPTY : *it; } // Search slice index for the closest slice to given slice_level. // max_epsilon gives the allowable deviation of the returned slice record's - // level. + // level. Use SliceRecord::is_valid() to check the result. const SliceRecord& closest_slice_to_slice_level( - float slice_level, float max_epsilon = float(EPSILON)) const + float slice_level, + float max_epsilon = std::numeric_limits::max()) const { auto it = closest_slice_record(m_slice_index, slice_level, max_epsilon); - if (it == m_slice_index.end()) return SliceRecord::EMPTY; - return *it; + return it == m_slice_index.end() ? SliceRecord::EMPTY : *it; } protected: diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 78d1085e8d..da62ddab08 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5027,7 +5027,7 @@ void GLCanvas3D::_render_sla_slices() const // Offset to avoid OpenGL Z fighting between the object's horizontal surfaces and the triangluated surfaces of the cuts. double plane_shift_z = 0.002; - if (! slice_low.is_valid()) { + if (slice_low.is_valid()) { const ExPolygons& obj_bottom = slice_low.get_slice(soModel); const ExPolygons& sup_bottom = slice_low.get_slice(soSupport); // calculate model bottom cap @@ -5038,7 +5038,7 @@ void GLCanvas3D::_render_sla_slices() const bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, true); } - if (! slice_high.is_valid()) { + if (slice_high.is_valid()) { const ExPolygons& obj_top = slice_high.get_slice(soModel); const ExPolygons& sup_top = slice_high.get_slice(soSupport); // calculate model top cap From a0f18f9f7c3ae3dfbf4ac8f0682801a20dbf51b8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Mar 2019 17:16:50 +0100 Subject: [PATCH 13/14] Add check for missing support points if supports are enabled. --- src/libslic3r/SLAPrint.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 79834908f2..c5824f2fc5 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -587,6 +587,15 @@ void swapXY(ExPolygon& expoly) { std::string SLAPrint::validate() const { for(SLAPrintObject * po : m_objects) { + + const ModelObject *mo = po->model_object(); + + if(po->config().supports_enable.getBool() && + mo->sla_points_status == sla::PointsStatus::UserModified && + mo->sla_support_points.empty()) + return L("Cannot proceed without support points! " + "Add support points or disable support generation."); + sla::SupportConfig cfg = make_support_cfg(po->config()); double pinhead_width = @@ -596,7 +605,7 @@ std::string SLAPrint::validate() const cfg.head_penetration_mm; if(pinhead_width > cfg.object_elevation_mm) - return L("Elevetion is too low for object."); + return L("Elevation is too low for object."); } return ""; From 937f6112ce64255db980c0b1df9818d0a52a4a33 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 26 Mar 2019 17:57:54 +0100 Subject: [PATCH 14/14] imgui: Fix assert caused by set_language() while new frame is open --- src/slic3r/GUI/ImGuiWrapper.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 0050b64632..0932c8ce93 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -56,6 +56,14 @@ bool ImGuiWrapper::init() void ImGuiWrapper::set_language(const std::string &language) { + if (m_new_frame_open) { + // ImGUI internally locks the font between NewFrame() and EndFrame() + // NewFrame() might've been called here because of input from the 3D scene; + // call EndFrame() + ImGui::EndFrame(); + m_new_frame_open = false; + } + const ImWchar *ranges = nullptr; size_t idx = language.find('_'); std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx);