diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c49ea417c..03dd6045c 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 8c7a0fa97..42317aa2e 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 3192e7861..c6a90a8cc 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 2361ff6d3..18324f2e8 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);