diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 102a991ca..14f34dbc6 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -10,6 +10,9 @@ namespace Slic3r { class Layer; +using LayerPtrs = std::vector; +class LayerRegion; +using LayerRegionPtrs = std::vector; class PrintRegion; class PrintObject; @@ -93,9 +96,6 @@ private: const PrintRegion *m_region; }; - -typedef std::vector LayerRegionPtrs; - class Layer { public: @@ -162,6 +162,8 @@ public: protected: friend class PrintObject; + friend std::vector new_layers(PrintObject*, const std::vector&); + friend std::string fix_slicing_errors(LayerPtrs&, const std::function&); Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) : upper_layer(nullptr), lower_layer(nullptr), slicing_errors(false), diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 1b3265084..3cbf684a1 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -827,7 +827,7 @@ inline void model_volumes_sort_by_id(ModelVolumePtrs &model_volumes) inline const ModelVolume* model_volume_find_by_id(const ModelVolumePtrs &model_volumes, const ObjectID id) { auto it = lower_bound_by_predicate(model_volumes.begin(), model_volumes.end(), [id](const ModelVolume *mv) { return mv->id() < id; }); - return it != model_volume.end() && (*it)->id() == id ? *it : nullptr; + return it != model_volumes.end() && (*it)->id() == id ? *it : nullptr; } enum ModelInstanceEPrintVolumeState : unsigned char diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 44d2896aa..a90c76a91 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -21,11 +21,11 @@ namespace Slic3r { -class Print; -class PrintObject; -class ModelObject; class GCode; class Layer; +class ModelObject; +class Print; +class PrintObject; class SupportLayer; namespace FillAdaptive { @@ -95,6 +95,10 @@ public: { m_config.apply_only(other, keys, ignore_nonexistent); m_config_hash = m_config.hash(); } private: friend Print; + friend void print_region_ref_inc(PrintRegion&); + friend void print_region_ref_reset(PrintRegion&); + friend int print_region_ref_cnt(const PrintRegion&); + PrintRegionConfig m_config; size_t m_config_hash; int m_print_region_id { -1 }; @@ -162,7 +166,7 @@ public: // clipped by a layer range modifier. struct VolumeExtents { ObjectID volume_id; - BoundingBoxf bbox; + BoundingBoxf3 bbox; }; struct VolumeRegion @@ -174,7 +178,7 @@ public: // Pointer to PrintObjectRegions::all_regions, null for a negative volume. PrintRegion *region { nullptr }; // Pointer to VolumeExtents::bbox. - const BoundingBoxf *bbox { nullptr }; + const BoundingBoxf3 *bbox { nullptr }; // To speed up merging of same regions. const VolumeRegion *prev_same_region { nullptr }; }; @@ -187,7 +191,7 @@ public: int parent { -1 }; // Pointer to PrintObjectRegions::all_regions. PrintRegion *region { nullptr }; - } + }; // One slice over the PrintObject (possibly the whole PrintObject) and a list of ModelVolumes and their bounding boxes // possibly clipped by the layer_height_range. @@ -200,29 +204,35 @@ public: // Volumes sorted by ModelVolume::id(). std::vector volumes; - bool has_volume(const ObjectID id) { - auto it = lower_bound_by_predicate(this->volumes.begin(), this->volumes.end(), [id](const VolumeExtents &v){ return v.volume_id == id; }); + // Sorted in the order of their source ModelVolumes, thus reflecting the order of region clipping, modifier overrides etc. + std::vector volume_regions; + std::vector painted_regions; + + bool has_volume(const ObjectID id) const { + auto it = lower_bound_by_predicate(this->volumes.begin(), this->volumes.end(), [id](const VolumeExtents& v) { return v.volume_id == id; }); return it != this->volumes.end() && it->volume_id == id; } - - // Sorted in the order of their source ModelVolumes, thus reflecting the order of region clipping, modifier overrides etc. - std::vector volume_regions; - std::vector painted_regions; }; std::vector> all_regions; std::vector layer_ranges; // Transformation of this ModelObject into one of the associated PrintObjects (all PrintObjects derived from a single modelObject differ by a Z rotation only). // This transformation is used to calculate VolumeExtents. - Matrix3x3f trafo_bboxes; + Transform3d trafo_bboxes; + std::vector cached_volume_ids; - size_t ref_cnt_inc() { ++ this->ref_cnt; } - size_t ref_cnt_dec() { if (-- this->ref_cnt == 0) delete *this; } + size_t ref_cnt_inc() { ++ m_ref_cnt; } + size_t ref_cnt_dec() { if (-- m_ref_cnt == 0) delete this; } + void clear() { + all_regions.clear(); + layer_ranges.clear(); + } private: + friend class PrintObject; // Number of PrintObjects generated from the same ModelObject and sharing the regions. // ref_cnt could only be modified by the main thread, thus it does not need to be atomic. - size_t m_ref_cnt; + size_t m_ref_cnt{ 0 }; }; class PrintObject : public PrintObjectBaseWithState @@ -254,13 +264,6 @@ public: bool has_brim() const { return this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.; } - - // adds region_id, too, if necessary - void add_region_volume(unsigned int region_id, int volume_id, const t_layer_height_range &layer_range) { - if (region_id >= m_region_volumes.size()) - m_region_volumes.resize(region_id + 1); - m_region_volumes[region_id].volumes.push_back({ layer_range, volume_id }); - } // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id // since they have different semantics. @@ -299,8 +302,8 @@ public: const SlicingParameters& slicing_parameters() const { return m_slicing_params; } static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object, float object_max_z); - size_t num_printing_regions() const throw() { return m_all_regions.size(); } - const PrintRegion& printing_region(size_t idx) const throw() { return *m_all_regions[idx]; } + size_t num_printing_regions() const throw() { return m_shared_regions->all_regions.size(); } + const PrintRegion& printing_region(size_t idx) const throw() { return *m_shared_regions->all_regions[idx].get(); } //FIXME returing all possible regions before slicing, thus some of the regions may not be slicing at the end. std::vector> all_regions() const; @@ -327,7 +330,7 @@ private: friend class Print; PrintObject(Print* print, ModelObject* model_object, const Transform3d& trafo, PrintInstances&& instances); - ~PrintObject() { if (m_shared_regions && -- m_shared_regions->ref_cnt == 0) delete m_shared_regions; } + ~PrintObject() { if (m_shared_regions && -- m_shared_regions->m_ref_cnt == 0) delete m_shared_regions; } void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { m_config.apply(other, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { m_config.apply_only(other, keys, ignore_nonexistent); } @@ -344,7 +347,6 @@ private: void update_slicing_parameters(); static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders); - static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders); private: void make_perimeters(); @@ -378,7 +380,7 @@ private: // Object split into layer ranges and regions with their associated configurations. // Shared among PrintObjects created for the same ModelObject. - PrintObjectRegions m_shared_regions; + PrintObjectRegions *m_shared_regions; SlicingParameters m_slicing_params; LayerPtrs m_layers; diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index b19a5b3a1..3dc27a5b5 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -293,6 +293,7 @@ public: }; LayerRanges() = default; + LayerRanges(const t_layer_config_ranges &in) { this->assign(in); } // Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs. void assign(const t_layer_config_ranges &in) { @@ -310,7 +311,7 @@ public: if (range.first.second > last_z + EPSILON) { const DynamicPrintConfig *cfg = &range.second.get(); m_ranges.push_back({ t_layer_height_range(last_z, range.first.second), cfg }); - last_z = range.layer_height_range.second; + last_z = range.first.second; } } if (m_ranges.empty()) @@ -328,13 +329,16 @@ public: // assert(it == m_ranges.end() || std::abs(it->first.first - range.first ) < EPSILON); // assert(it == m_ranges.end() || std::abs(it->first.second - range.second) < EPSILON); if (it == m_ranges.end() || - std::abs(it->first.first - range.first) > EPSILON || - std::abs(it->first.second - range.second) > EPSILON ) + std::abs(it->layer_height_range.first - range.first) > EPSILON || + std::abs(it->layer_height_range.second - range.second) > EPSILON ) return nullptr; // desired range doesn't found - return (it == m_ranges.end()) ? nullptr : it->second; + return (it == m_ranges.end()) ? nullptr : it->config; } + std::vector::const_iterator begin() const { return m_ranges.cbegin(); } std::vector::const_iterator end () const { return m_ranges.cend(); } + size_t size () const { return m_ranges.size(); } + private: // Layer ranges with their config overrides and list of volumes with their snug bounding boxes in a given layer range. std::vector m_ranges; @@ -358,7 +362,7 @@ struct ModelObjectStatus { }; ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {} - ~ModelObjectStatus() { if (print_object_regions) print_object_regions.ref_cnt_dec(); } + ~ModelObjectStatus() { if (print_object_regions) print_object_regions->ref_cnt_dec(); } // Key of the set. ObjectID id; @@ -377,11 +381,11 @@ struct ModelObjectStatus { struct ModelObjectStatusDB { - void add(const ModelObject &model_object, const ModelObjectStatus status) { + void add(const ModelObject &model_object, const ModelObjectStatus::Status status) { db.emplace(model_object.id(), status); } - bool add_if_new(const ModelObject &model_object, const ModelObjectStatus status) { + bool add_if_new(const ModelObject &model_object, const ModelObjectStatus::Status status) { auto it = db.find(ModelObjectStatus(model_object.id())); assert(it != db.end()); if (it == db.end()) { @@ -404,7 +408,7 @@ struct ModelObjectStatusDB } std::set db; -} +}; struct PrintObjectStatus { enum Status { @@ -413,36 +417,34 @@ struct PrintObjectStatus { Reused, New }; + PrintObjectStatus(PrintObject *print_object, Status status = Unknown) : id(print_object->model_object()->id()), print_object(print_object), trafo(print_object->trafo()), status(status) {} PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} + // ID of the ModelObject & PrintObject - ObjectID id; + ObjectID id; // Pointer to the old PrintObject PrintObject *print_object; // Trafo generated with model_object->world_matrix(true) Transform3d trafo; Status status; + // Search by id. bool operator<(const PrintObjectStatus &rhs) const { return id < rhs.id; } }; -template -iter_pair make_range(std::pair p) { - return iter_pair(p); -} - class PrintObjectStatusDB { public: using iterator = std::multiset::iterator; using const_iterator = std::multiset::const_iterator; - PrintObjectStatusDB(const PrintObjects &print_objects) { - for (PrintObject *print_object : m_objects) - db.emplace(PrintObjectStatus(print_object)); + PrintObjectStatusDB(const PrintObjectPtrs &print_objects) { + for (PrintObject *print_object : print_objects) + m_db.emplace(PrintObjectStatus(print_object)); } struct iterator_range : std::pair @@ -454,26 +456,26 @@ public: }; iterator_range get_range(const ModelObject &model_object) { - return print_object_status.equal_range(PrintObjectStatus(model_object.id())); + return m_db.equal_range(PrintObjectStatus(model_object.id())); } iterator_range get_range(const ModelObjectStatus &model_object_status) { - return print_object_status.equal_range(PrintObjectStatus(model_object_status.id)); + return m_db.equal_range(PrintObjectStatus(model_object_status.id)); } size_t count(const ModelObject &model_object) { - return db.count(PrintObjectStatus(model_object.id())); + return m_db.count(PrintObjectStatus(model_object.id())); } + std::multiset::iterator begin() { return m_db.begin(); } + std::multiset::iterator end() { return m_db.end(); } + void clear() { - db.clear(); + m_db.clear(); } - const_iterator begin() throw() { return this->first; } - const_iterator end() throw() { return this->second; } - private: - std::multiset db; + std::multiset m_db; }; static inline bool model_volume_needs_bbox(const ModelVolume &mv) @@ -511,28 +513,33 @@ static inline bool trafos_differ_in_rotation_and_mirroring_by_z_only(const Trans return std::abs(d) > EPSILON; } -static BoundingBoxf3 transformed_its_bbox2d(const stl_triangle_vertex_indices &its, const Matrix3f &m) +static BoundingBoxf3 transformed_its_bbox2d(const indexed_triangle_set &its, const Matrix3f &m, float offset) { BoundingBoxf3 bbox; for (const stl_triangle_vertex_indices &tri : its.indices) for (int i = 0; i < 3; ++ i) bbox.merge(m * its.vertices[tri(i)]); + bbox.min.x() -= offset; + bbox.min.y() -= offset; + bbox.min.x() += offset; + bbox.min.y() += offset; return bbox; } static void transformed_its_bboxes_in_z_ranges( - const stl_triangle_vertex_indices &its, + const indexed_triangle_set &its, const Matrix3f &m, const std::vector &z_ranges, - std::vector &bboxes) + std::vector &bboxes, + const float offset) { - bboxes.assign(z_ranges.size(), BoundingBoxf()); + bboxes.assign(z_ranges.size(), BoundingBoxf3()); for (const stl_triangle_vertex_indices &tri : its.indices) { const Vec3f pts[3] = { m * its.vertices[tri(0)], m * its.vertices[tri(1)], m * its.vertices[tri(2)] }; for (size_t irange = 0; irange < z_ranges.size(); ++ irange) { - t_layer_height_range &z_range = z_ranges[irange]; - BoundingBoxf3 &bbox = bboxes[irange]; - int iprev = 3; + const t_layer_height_range &z_range = z_ranges[irange]; + BoundingBoxf3 &bbox = bboxes[irange]; + int iprev = 2; for (int iedge = 0; iedge < 3; ++ iedge) { const Vec3f *p1 = &pts[iprev]; const Vec3f *p2 = &pts[iedge]; @@ -545,24 +552,24 @@ static void transformed_its_bboxes_in_z_ranges( if (p1->z() > z_range.second) { // Two intersections. float zspan = p2->z() - p1->z(); - float t1 = z_range.first / zspan; - float t2 = z_range.second / zspan; + float t1 = (z_range.first - p1->z()) / zspan; + float t2 = (z_range.second - p1->z()) / zspan; Vec2f p = to_2d(*p1); Vec2f v(p2->x() - p1->x(), p2->y() - p1->y()); - bbox.merge(to_3d(p + v * t1, z_range.first)); - bbox.merge(to_3d(p + v * t2, z_range.second)); + bbox.merge(to_3d((p + v * t1).eval(), float(z_range.first))); + bbox.merge(to_3d((p + v * t2).eval(), float(z_range.second))); } else { // Single intersection with the lower limit. - float t = z_range.first / (p2->z() - p1->z()); + float t = (z_range.first - p1->z()) / (p2->z() - p1->z()); Vec2f v(p2->x() - p1->x(), p2->y() - p1->y()); - bbox.merge(to_3d(to_2d(*p1) + v * t), z_range.first); + bbox.merge(to_3d((to_2d(*p1) + v * t).eval(), float(z_range.first))); bbox.merge(*p2); } } else if (p2->z() > z_range.second) { // Single intersection with the upper limit. - float t = z_range.second / (p2->z() - p1->z()); + float t = (z_range.second - p1->z()) / (p2->z() - p1->z()); Vec2f v(p2->x() - p1->x(), p2->y() - p1->y()); - bbox.merge(to_3d(to_2d(*p1) + v * t), z_range.second); + bbox.merge(to_3d((to_2d(*p1) + v * t).eval(), float(z_range.second))); bbox.merge(*p1); } else { // Both points are inside. @@ -573,204 +580,132 @@ static void transformed_its_bboxes_in_z_ranges( } } } -} -// Update from scratch. -void update_model_object_status_layers_bboxes( - const ModelObject &model_object, - ModelObjectStatus &model_object_status) -{ - assert(! model_object_status.print_instances.empty()); - assert(! model_object_status.layer_ranges.m_ranges.empty()); - - // Use the trafo of the 1st newly created PrintObject. - model_object_status.layer_ranges.m_trafo = model_object_status.print_instances.front().trafo; - - if (model_object_status.layer_ranges.m_ranges.size() == 1) { - LayerRange &layer_range = model_object_status.layer_ranges.m_ranges.front(); - assert(layer_range.volumes.empty()); - layer_range.reserve(model_object.volumes.size()); - for (const ModelVolume *model_volume : model_object.volumes) - if (model_volume_needs_bbox(*model_volume)) - layer_range.volumes.emplace_back(model_volume->id(), - transformed_its_bbox2d(model_volume.mesh().its, trafo_for_bbox(model_object_status.layer_ranges.m_trafo, model_volume->get_matrix(false)))); - } else { - std::vector bboxes; - std::vector ranges; - ranges.reserve(model_object_status.layer_ranges.m_ranges.size()); - for (const LayerRange &layer_range : model_object_status.layer_ranges.m_ranges) { - assert(layer_range.volumes.empty()); - t_layer_height_range r = layer_range.layer_height_range; - r.first -= EPSILON; - r.second += EPSILON; - ranges.emplace_back(r); - } - double offset = print_object.config().xy_size_compensation(); - for (const ModelVolume *model_volume : model_object.volumes) - if (model_volume_needs_bbox(*model_volume)) { - transformed_its_bbox2ds_in_z_ranges(model_volume.mesh().its, trafo_for_bbox(model_object_status.layer_ranges.m_trafo, model_volume->get_matrix(false)), - ranges, bboxes); - size_t i = 0; - for (LayerRange &layer_range : model_object_status.layer_ranges.m_ranges) { - if (bboxes[i].defined) { - bboxes.min.x() -= offset; - bboxes.min.y() -= offset; - bboxes.max.x() += offset; - bboxes.max.y() += offset; - layer_range.volumes.emplace_back(model_volume->id(), bboxes[i]); - } - ++ i; - } - } -} - -// Update by reusing everything. -void update_model_object_status_layers_bboxes( - const ModelObject &model_object, - PrintObject &&print_object, - ModelObjectStatus &model_object_status) -{ - assert(! model_object_status.print_instances.empty()); - assert(! model_object_status.layer_ranges.m_ranges.empty()); - - for (print_object.m_volumes_ranges -} - -// Try to extract layer ranges and snug bounding boxes from some existing PrintObject. -void update_model_object_status_layers_bboxes( - const ModelObject &model_object_old, - const ModelObject &model_object_new, - PrintObject &&print_object, - ModelObjectStatus &model_object_status) -{ - assert(! print_object.m_region_volumes.empty()); - - // 1) Verify that the trafo is still applicable. - if (trafos_differ_in_rotation_and_mirroring_by_z_only(model_object_status.layer_ranges.m_trafo, model_object_status.print_instances.front().trafo)) { - update_model_object_status_layers_bboxes(model_object_new, model_object_status); - return; + for (BoundingBoxf3 &bbox : bboxes) { + bbox.min.x() -= offset; + bbox.min.y() -= offset; + bbox.min.x() += offset; + bbox.min.y() += offset; } - - // 2) Try to recover some of the trafos from print_object. - } -void print_objects_regions_update_volumes(PrintObjectRegions &print_object_regions, ModelVolumePtrs old_volumes, ModelVolumePtrs new_volumes) +// Last PrintObject for this print_object_regions has been fully invalidated (deleted). +// Keep print_object_regions, but delete those volumes, which were either removed from new_volumes, or which rotated or scaled, so they need +// their bounding boxes to be recalculated. +void print_objects_regions_invalidate_keep_some_volumes(PrintObjectRegions &print_object_regions, ModelVolumePtrs old_volumes, ModelVolumePtrs new_volumes) { + assert(old_volumes.size() == print_object_regions.cached_volume_ids.size()); + print_object_regions.all_regions.clear(); model_volumes_sort_by_id(old_volumes); model_volumes_sort_by_id(new_volumes); - { - size_t last = 0; - size_t i_old = 0; - for (size_t i_new = 0; i_new < new_volumes.size(); ++ i_new) { - for (; i_old < old_volumes.size(); ++ i_old) - if (old_volumes[i_old]->id() == new_volumes[i_new]->id()) - break; - if (i_old == old_volumes.size()) + + size_t last = 0; + size_t i_old = 0; + for (size_t i_new = 0; i_new < new_volumes.size(); ++ i_new) { + for (; i_old < old_volumes.size(); ++ i_old) + if (old_volumes[i_old]->id() >= new_volumes[i_new]->id()) break; - if (old_volumes[i_old]->get_matrix().isApprox(new_volumes[i_new]->get_matrix())) { - // Reuse the volume. - new_volumes[last ++] = new_volumes[i_new]; - } else { - // Don't reuse the volume. - } + if (i_old == old_volumes.size()) + break; + if (old_volumes[i_old]->id() == new_volumes[i_new]->id() && old_volumes[i_old]->get_matrix().isApprox(new_volumes[i_new]->get_matrix())) { + // Reuse the volume. + print_object_regions.cached_volume_ids[last ++] = print_object_regions.cached_volume_ids[i_old]; + } else { + // Don't reuse the volume. } - old_volumes.clear(); - new_volumes.erase(new_volumes.begin() + last, new_volumes.end()); - } - - for (PrintObjectRegions::LayerRangeRegions &layer_regions : print_object_regions.layer_ranges) { - auto it = std::remove_if(layer_regions.volumes.begin(), layer_regions.volumes.end(), - [](const PrintObjectRegions::VolumeExtents &v) { return model_volume_find_by_id(new_volumes, v.volume_id) == nullptr; }); - layer_regions.volumes.erase(it, layer_regions.volumes.end()); - layer_regions.volume_regions.clear(); - layer_regions.volume_regions.clear(); - layer_regions.painted_regions.clear(); } + print_object_regions.cached_volume_ids.erase(print_object_regions.cached_volume_ids.begin() + last, print_object_regions.cached_volume_ids.end()); } -const BoundingBoxf3* find_volume(const PrintObjectLayerRange &layer_range, const ModelVolume &volume) +const BoundingBoxf3* find_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const ModelVolume &volume) { - auto it = lower_bound_by_predicate(layer_range.volumes.begin(), layer_range.volumes.end(), [&volume](const PrintVolumeExtents &v){ return v.volume_id < volume.id(); }); - retun it != layer_range.volumes.end() && it->volume_id == volume.id() ? &it->bbox : nullptr; + auto it = lower_bound_by_predicate(layer_range.volumes.begin(), layer_range.volumes.end(), [&volume](const PrintObjectRegions::VolumeExtents &v){ return v.volume_id < volume.id(); }); + return it != layer_range.volumes.end() && it->volume_id == volume.id() ? &it->bbox : nullptr; } -// Returns false if this object needs to be resliced. -static bool verify_update_print_object_regions( - ModelVolumePtrs model_volumes, - const PrintRegionConfig &default_region_config, - size_t num_extruders, - const std::vector &painting_extruders, - const PrintObjectRegions &print_object_regions, - std::function<> callback_invalidate) +PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders); + +void print_region_ref_inc(PrintRegion &r) { ++ r.m_ref_cnt; } +void print_region_ref_reset(PrintRegion &r) { r.m_ref_cnt = 0; } +int print_region_ref_cnt(const PrintRegion &r) { return r.m_ref_cnt; } + +// Verify whether the PrintRegions of a PrintObject are still valid, possibly after updating the region configs. +// Before region configs are updated, callback_invalidate() is called to possibly stop background processing. +// Returns false if this object needs to be resliced because regions were merged or split. +bool verify_update_print_object_regions( + ModelVolumePtrs model_volumes, + const PrintRegionConfig &default_region_config, + size_t num_extruders, + const std::vector &painting_extruders, + PrintObjectRegions &print_object_regions, + const std::function &callback_invalidate) { // Sort by ModelVolume ID. - std::sort(model_volumes.begin(), model_volumes.end(), [](const ModelVolume *l, const ModelVolume *r){ return l->id() < r->id(); }); + model_volumes_sort_by_id(model_volumes); - for (std::unique_ptr ®ion : all_regions) - region->m_ref_cnt = 0; + for (std::unique_ptr ®ion : print_object_regions.all_regions) + print_region_ref_reset(*region); // Verify and / or update PrintRegions produced by ModelVolumes, layer range modifiers, modifier volumes. - for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) - for (const PrintObjectRegions::VolumeRegion ®ion : layer_range.volume_regions) { - auto it_model_volume = lower_bound_by_predicate(model_volumes.begin(), model_volumes.end(), [®ion](const ModelVolume *v){ return v->id() < region.volume_id}); - assert(it_model_volume != model_volumes.begin() && it_model_volume->id() == region.volume_id); + for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) + for (PrintObjectRegions::VolumeRegion ®ion : layer_range.volume_regions) { + auto it_model_volume = lower_bound_by_predicate(model_volumes.begin(), model_volumes.end(), [®ion](const ModelVolume *v){ return v->id() < region.model_volume->id(); }); + assert(it_model_volume != model_volumes.end() && (*it_model_volume)->id() == region.model_volume->id()); PrintRegionConfig cfg = region.parent == -1 ? - PrintObject::region_config_from_model_volume(default_region_config, region.config, *it_model_volume, num_extruders) : - PrintObject::region_config_from_model_volume(parent_region.region->config(), nullptr, *it_model_volume, num_extruders); + region_config_from_model_volume(default_region_config, layer_range.config, **it_model_volume, num_extruders) : + region_config_from_model_volume(layer_range.volume_regions[region.parent].region->config(), nullptr, **it_model_volume, num_extruders); if (cfg != region.region->config()) { // Region configuration changed. - if (region.region->m_ref_cnt == 0) { + if (print_region_ref_cnt(*region.region) == 0) { // Region is referenced for the first time. Just change its parameters. // Stop the background process before assigning new configuration to the regions. - t_config_option_keys diff = override.region->config().diff(cfg); - callback_invalidate(override.region->config(), cfg, diff); - override.region->config().config_apply_only(cfg, diff, false); + t_config_option_keys diff = region.region->config().diff(cfg); + callback_invalidate(region.region->config(), cfg, diff); + region.region->config_apply_only(cfg, diff, false); } else { // Region is referenced multiple times, thus the region is being split. We need to reslice. return false; } } - ++ region.region->m_ref_cnt; + print_region_ref_inc(*region.region); } // Verify and / or update PrintRegions produced by color painting. - for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) + for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) { size_t painted_region_idx = 0; for (unsigned int painted_extruder_id : painting_extruders) for (int parent_region_id = 0; parent_region_id < int(layer_range.volume_regions.size()); ++ parent_region_id) { - const PrintVolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; - const PrintPaintedRegion ®ion = layer_range.painted_regions[painted_region_idx ++]; + const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; + const PrintObjectRegions::PaintedRegion ®ion = layer_range.painted_regions[painted_region_idx ++]; PrintRegionConfig cfg = parent_region.region->config(); - cfg.external_perimeter_extruder = painted_extruder_id; - cfg.perimeter_extruder = painted_extruder_id; - cfg.infill_extruder = painted_extruder_id; + cfg.perimeter_extruder.value = painted_extruder_id; + cfg.infill_extruder.value = painted_extruder_id; if (cfg != region.region->config()) { // Region configuration changed. - if (region->m_ref_cnt == 0) { + if (print_region_ref_cnt(*region.region) == 0) { // Region is referenced for the first time. Just change its parameters. // Stop the background process before assigning new configuration to the regions. - t_config_option_keys diff = override.region->config().diff(cfg); - callback_invalidate(override.region->config(), cfg, diff); - override.region->config().config_apply_only(cfg, diff, false); + t_config_option_keys diff = region.region->config().diff(cfg); + callback_invalidate(region.region->config(), cfg, diff); + region.region->config_apply_only(cfg, diff, false); } else { // Region is referenced multiple times, thus the region is being split. We need to reslice. return false; } } - ++ region.region->m_ref_cnt; + print_region_ref_inc(*region.region); } } // Lastly verify, whether some regions were not merged. { std::vector regions; - regions.reserve(all_regions.size()); - for (std::unique_ptr ®ion : all_regions) { - assert(region->m_ref_cnt > 0); - regions.emplace_back(&(*region)); + regions.reserve(print_object_regions.all_regions.size()); + for (std::unique_ptr ®ion : print_object_regions.all_regions) { + assert(print_region_ref_cnt(*region) > 0); + regions.emplace_back(&(*region.get())); } std::sort(regions.begin(), regions.end(), [](const PrintRegion *l, const PrintRegion *r){ return l->config_hash() < r->config_hash(); }); for (size_t i = 0; i < regions.size(); ++ i) { @@ -784,53 +719,143 @@ static bool verify_update_print_object_regions( } } - return out; + return true; +} + +// Update caches of volume bounding boxes. +void update_volume_bboxes( + std::vector &layer_ranges, + std::vector &cached_volume_ids, + ModelVolumePtrs model_volumes, + const Transform3d &object_trafo, + const float offset) +{ + // output will be sorted by the order of model_volumes sorted by their ObjectIDs. + model_volumes_sort_by_id(model_volumes); + + if (layer_ranges.size() == 1) { + PrintObjectRegions::LayerRangeRegions &layer_range = layer_ranges.front(); + std::vector volumes_old(std::move(layer_range.volumes)); + layer_range.volumes.reserve(model_volumes.size()); + for (const ModelVolume *model_volume : model_volumes) + if (model_volume_needs_bbox(*model_volume)) { + if (std::binary_search(cached_volume_ids.begin(), cached_volume_ids.end(), model_volume->id())) { + auto it = lower_bound_by_predicate(volumes_old.begin(), volumes_old.end(), [model_volume](PrintObjectRegions::VolumeExtents &v) { return v.volume_id == model_volume->id(); }); + if (it != volumes_old.end() && it->volume_id == model_volume->id()) + layer_range.volumes.emplace_back(*it); + } else + layer_range.volumes.emplace_back(model_volume->id(), + transformed_its_bbox2d(model_volume->mesh().its, trafo_for_bbox(object_trafo, model_volume->get_matrix(false)), offset)); + } + } else { + std::vector> volumes_old; + if (cached_volume_ids.empty()) + for (PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges) + layer_range.volumes.clear(); + else { + volumes_old.reserve(layer_ranges.size()); + for (PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges) + volumes_old.emplace_back(std::move(layer_range.volumes)); + } + + std::vector bboxes; + std::vector ranges; + ranges.reserve(layer_ranges.size()); + for (const PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges) { + t_layer_height_range r = layer_range.layer_height_range; + r.first -= EPSILON; + r.second += EPSILON; + ranges.emplace_back(r); + } + for (const ModelVolume *model_volume : model_volumes) + if (model_volume_needs_bbox(*model_volume)) { + if (std::binary_search(cached_volume_ids.begin(), cached_volume_ids.end(), model_volume->id())) { + for (PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges) { + const auto &vold = volumes_old[&layer_range - layer_ranges.data()]; + auto it = lower_bound_by_predicate(vold.begin(), vold.end(), [model_volume](const PrintObjectRegions::VolumeExtents &v) { return v.volume_id == model_volume->id(); }); + if (it != vold.end() && it->volume_id == model_volume->id()) + layer_range.volumes.emplace_back(*it); + } + } else { + transformed_its_bboxes_in_z_ranges(model_volume->mesh().its, trafo_for_bbox(object_trafo, model_volume->get_matrix(false)), ranges, bboxes, offset); + for (PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges) + layer_range.volumes.emplace_back(model_volume->id(), bboxes[&layer_range - layer_ranges.data()]); + } + } + } + + cached_volume_ids.clear(); + cached_volume_ids.reserve(model_volumes.size()); + for (const ModelVolume *v : model_volumes) + cached_volume_ids.emplace_back(v->id()); } // Either a fresh PrintObject, or PrintObject regions were invalidated (merged, split). // Generate PrintRegions from scratch. -static PrintObjectRegions generate_print_object_regions( +static PrintObjectRegions* generate_print_object_regions( + PrintObjectRegions *print_object_regions_old, const ModelVolumePtrs &model_volumes, - const std::vector &layer_ranges, + const LayerRanges &model_layer_ranges, const PrintRegionConfig &default_region_config, + const Transform3d &trafo, size_t num_extruders, const std::vector &painting_extruders) { - PrintObjectRegions out; - auto &all_regions = all_regions; - auto &layer_ranges_regions = layer_ranges; + // Reuse the old object or generate a new one. + auto out = print_object_regions_old ? std::unique_ptr(print_object_regions_old) : std::make_unique(); + auto &all_regions = out->all_regions; + auto &layer_ranges_regions = out->layer_ranges; - layer_ranges_regions.assign(layer_ranges.size(), PrintObjectLayerRangeRegions{}); - for (PrintObjectLayerRangeRegions &lrr : layer_ranges_regions) - lrr = layer_ranges[&lrr - layer_ranges_regions.data()]; + bool reuse_old = print_object_regions_old && !print_object_regions_old->layer_ranges.empty(); - std::set region_set; - auto get_create_region = [®ion_set](PrintRegionConfig &&config) { + if (reuse_old) { +#ifndef NDEBUG + // Verify that the old ranges match the new ranges. + assert(model_layer_ranges.size() == layer_ranges_regions.size()); + for (const auto &range : model_layer_ranges) { + const PrintObjectRegions::LayerRangeRegions &r = layer_ranges_regions[&range - &*(model_layer_ranges.end())]; + assert(range.layer_height_range == r.layer_height_range); + assert(range.config == r.config); + assert(r.volume_regions.empty()); + assert(r.painted_regions.empty()); + } +#endif // NDEBUG + } else { + out->trafo_bboxes = trafo; + layer_ranges_regions.reserve(model_layer_ranges.size()); + for (const auto &range : model_layer_ranges) + layer_ranges_regions.push_back({ range.layer_height_range, range.config }); + } + update_volume_bboxes(layer_ranges_regions, out->cached_volume_ids, model_volumes, out->trafo_bboxes, std::max(0.f, float(print_object.config().xy_size_compensation())); + + std::set region_set; + auto get_create_region = [®ion_set](PrintRegionConfig &&config) -> PrintRegion* { + return nullptr; }; // Chain the regions in the order they are stored in the volumes list. for (int volume_id = 0; volume_id < int(model_volumes.size()); ++ volume_id) { - const ModelVolume &volume = model_volumes[volume_id]; + const ModelVolume &volume = *model_volumes[volume_id]; if (volume.type() == ModelVolumeType::MODEL_PART || volume.type() == ModelVolumeType::PARAMETER_MODIFIER) { - for (const PrintObjectLayerRangeRegions &layer_range : layer_ranges_regions) - if (const BoundingBoxf3 *bbox = find_volume(layer_range, volume); bbox) { - if (volume.type() == MODEL_PART) - layer_range.regions.push_back( { - volume.id(), -1, - get_create_region(PrintObject::region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders)) + for (PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges_regions) + if (const BoundingBoxf3 *bbox = find_volume_extents(layer_range, volume); bbox) { + if (volume.is_model_part()) + layer_range.volume_regions.push_back({ + &volume, -1, + get_create_region(region_config_from_model_volume(default_region_config, layer_range.config, volume, num_extruders)) }); else { - assert(volume.type() == ModelVolumeType::PARAMETER_MODIFIER); + assert(volume.is_modifier()); // Modifiers may be chained one over the other. Check for overlap, merge DynamicPrintConfigs. - for (int parent_region_id = int(layer_range.regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) { - const PrintVolumeRegion &parent_region = layer_range.regions[parent_region_id]; - const BoundingBoxf3 *parent_bbox = find_volume(layer_range, parent_region.volume_id); + for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) { + const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; + const BoundingBoxf3 *parent_bbox = find_volume_extents(layer_range, *parent_region.model_volume); assert(parent_bbox != nullptr); if (parent_bbox->overlap(*bbox)) - layer_range.regions.push_back( { - volume.id(), parent_region_id, - get_create_region(PrintObject::region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders)) + layer_range.volume_regions.push_back( { + &volume, parent_region_id, + get_create_region(region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders)) }); } } @@ -839,19 +864,17 @@ static PrintObjectRegions generate_print_object_regions( } // Finally add painting regions. - for (const PrintObjectLayerRangeRegions &layer_range : layer_ranges_regions) { + for (PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges_regions) for (unsigned int painted_extruder_id : painting_extruders) - for (int parent_region_id = 0; parent_region_id < int(layer_range.regions.size()); ++ parent_region_id) { - const PrintVolumeRegion &parent_region = layer_range.regions[parent_region_id]; + for (int parent_region_id = 0; parent_region_id < int(layer_range.volume_regions.size()); ++ parent_region_id) { + const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id]; PrintRegionConfig cfg = parent_region.region->config(); - cfg.external_perimeter_extruder = painted_extruder_id; - cfg.perimeter_extruder = painted_extruder_id; - cfg.infill_extruder = painted_extruder_id; + cfg.perimeter_extruder.value = painted_extruder_id; + cfg.infill_extruder.value = painted_extruder_id; layer_range.painted_regions.push_back({ painted_extruder_id, parent_region_id, get_create_region(std::move(cfg))}); } - } - return out; + return out.release(); } Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_config) @@ -1021,14 +1044,14 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // 3) Synchronize ModelObjects & PrintObjects. for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { ModelObject &model_object = *m_model.objects[idx_model_object]; - ModelObjectStatus &model_object_status = const_cast(model_object_status.reuse(model_object)); + ModelObjectStatus &model_object_status = const_cast(model_object_status_db.reuse(model_object)); const ModelObject &model_object_new = *model.objects[idx_model_object]; - model_object_status.print_instances = print_objects_from_model_object(*model_object_new); - if (it_status->status == ModelObjectStatus::New) + model_object_status.print_instances = print_objects_from_model_object(model_object_new); + if (model_object_status.status == ModelObjectStatus::New) // PrintObject instances will be added in the next loop. continue; // Update the ModelObject instance, possibly invalidate the linked PrintObjects. - assert(it_status->status == ModelObjectStatus::Old || it_status->status == ModelObjectStatus::Moved); + assert(model_object_status.status == ModelObjectStatus::Old || model_object_status.status == ModelObjectStatus::Moved); // Check whether a model part volume was added or removed, their transformations or order changed. // Only volume IDs, volume types, transformation matrices and their order are checked, configuration and other parameters are NOT checked. bool model_parts_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::MODEL_PART); @@ -1036,27 +1059,31 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ bool supports_differ = model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_BLOCKER) || model_volume_list_changed(model_object, model_object_new, ModelVolumeType::SUPPORT_ENFORCER); bool layer_height_ranges_differ = ! layer_height_ranges_equal(model_object.layer_config_ranges, model_object_new.layer_config_ranges, model_object_new.layer_height_profile.empty()); - auto print_objects_range = print_object_status_db.get_range(&model_object); + auto print_objects_range = print_object_status_db.get_range(model_object); assert(print_objects_range.begin() != print_objects_range.end()); - model_object_status.prints_objects_regions = (*print_objects_range.begin())->prints_objects_regions; - model_object_status.prints_objects_regions->ref_cnt_inc(); + // All PrintObjects in print_objects_range shall point to the same prints_objects_regions + model_object_status.print_object_regions = print_objects_range.begin()->print_object->m_shared_regions; + model_object_status.print_object_regions->ref_cnt_inc(); if (model_parts_differ || modifiers_differ || model_object.origin_translation != model_object_new.origin_translation || ! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile) || layer_height_ranges_differ) { // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. - model_object_status.prints_objects_regions_status = - model_object.origin_translation == model_object_new.origin_translation && ! layer_height_ranges_differ ? + model_object_status.print_object_regions_status = + model_object.origin_translation == model_object_new.origin_translation && ! layer_height_ranges_differ ? // Drop print_objects_regions. ModelObjectStatus::PrintObjectRegionsStatus::Invalid : // Reuse bounding boxes of print_objects_regions for ModelVolumes with unmodified transformation. ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid; - for (auto it : print_objects_range) { - update_apply_status(it->print_object->invalidate_all_steps()); - const_cast(*it).status = PrintObjectStatus::Deleted; + for (const PrintObjectStatus &print_object_status : print_objects_range) { + update_apply_status(print_object_status.print_object->invalidate_all_steps()); + const_cast(print_object_status).status = PrintObjectStatus::Deleted; } - if (model_object_status.prints_objects_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid) - print_objects_regions_update_volumes(*model_object_status.prints_objects_regions, model_object.volumes, model_object_new.volumes); + if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid) + // Drop everything from PrintObjectRegions but those VolumeExtents (of their particular ModelVolumes) that are still valid. + print_objects_regions_invalidate_keep_some_volumes(*model_object_status.print_object_regions, model_object.volumes, model_object_new.volumes); + else + model_object_status.print_object_regions->clear(); // Copy content of the ModelObject including its ID, do not change the parent. model_object.assign_copy(model_object_new); } else if (supports_differ || model_custom_supports_data_changed(model_object, model_object_new)) { @@ -1066,8 +1093,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(false); } // Invalidate just the supports step. - for (auto it : print_objects_range) - update_apply_status(it->print_object->invalidate_step(posSupportMaterial)); + for (const PrintObjectStatus &print_object_status : print_objects_range) + update_apply_status(print_object_status.print_object->invalidate_step(posSupportMaterial)); if (supports_differ) { // Copy just the support volumes. model_volume_list_update_supports(model_object, model_object_new); @@ -1082,11 +1109,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ model_object.config.assign_config(model_object_new.config); if (! object_diff.empty() || object_config_changed || num_extruders_changed) { PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); - for (auto it : print_object_status_db.get_range(model_object)) { - t_config_option_keys diff = it->print_object->config().diff(new_config); + for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(model_object)) { + t_config_option_keys diff = print_object_status.print_object->config().diff(new_config); if (! diff.empty()) { - update_apply_status(it->print_object->invalidate_state_by_config_options(it->print_object->config(), new_config, diff)); - it->print_object->config_apply_only(new_config, diff, true); + update_apply_status(print_object_status.print_object->invalidate_state_by_config_options(print_object_status.print_object->config(), new_config, diff)); + print_object_status.print_object->config_apply_only(new_config, diff, true); } } } @@ -1133,16 +1160,16 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ bool new_objects = false; // Walk over all new model objects and check, whether there are matching PrintObjects. for (ModelObject *model_object : m_model.objects) { - ModelObjectStatus &model_object_status = const_cast(model_object_status.reuse(model_object)); + ModelObjectStatus &model_object_status = const_cast(model_object_status_db.reuse(*model_object)); std::vector old; - old.reserve(print_object_status.count(*model_object)); - for (auto it : print_object_status.get_range(*model_object)) - if (it->status != PrintObjectStatus::Deleted) - old.emplace_back(&(*it)); + old.reserve(print_object_status_db.count(*model_object)); + for (const PrintObjectStatus &print_object_status : print_object_status_db.get_range(*model_object)) + if (print_object_status.status != PrintObjectStatus::Deleted) + old.emplace_back(&print_object_status); // Generate a list of trafos and XY offsets for instances of a ModelObject // Producing the config for PrintObject on demand, caching it at print_object_last. const PrintObject *print_object_last = nullptr; - auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject* print_object) { + auto print_object_apply_config = [this, &print_object_last, model_object, num_extruders](PrintObject *print_object) { print_object->config_apply(print_object_last ? print_object_last->config() : PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders)); @@ -1164,7 +1191,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ std::sort(old.begin(), old.end(), [](const PrintObjectStatus *lhs, const PrintObjectStatus *rhs){ return transform3d_lower(lhs->trafo, rhs->trafo); }); // Merge the old / new lists. auto it_old = old.begin(); - size_t num_old = old.size(); for (PrintObjectTrafoAndInstances &new_instances : model_object_status.print_instances) { for (; it_old != old.end() && transform3d_lower((*it_old)->trafo, new_instances.trafo); ++ it_old); if (it_old == old.end() || ! transform3d_equal((*it_old)->trafo, new_instances.trafo)) { @@ -1174,11 +1200,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ print_objects_new.emplace_back(print_object); // print_object_status.emplace(PrintObjectStatus(print_object, PrintObjectStatus::New)); new_objects = true; - if (it_old != old.end()) { - if (-- num_old == 0) - update_model_object_status_layers_bboxes(*model_object, model_object_status, std::move(*old.front()->print_object)); + if (it_old != old.end()) const_cast(*it_old)->status = PrintObjectStatus::Deleted; - } } else { // The PrintObject already exists and the copies differ. PrintBase::ApplyStatus status = (*it_old)->print_object->set_instances(std::move(new_instances.instances)); @@ -1195,7 +1218,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ m_objects = print_objects_new; // Delete the PrintObjects marked as Unknown or Deleted. bool deleted_objects = false; - for (auto &pos : print_object_status_db) + for (const PrintObjectStatus &pos : print_object_status_db) if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { update_apply_status(pos.print_object->invalidate_all_steps()); delete pos.print_object; @@ -1207,25 +1230,30 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(false); print_regions_reshuffled = true; } - print_object_status.clear(); + print_object_status_db.clear(); } // All regions now have distinct settings. - // Check whether applying the new region config defaults we would get different regions. + // Check whether applying the new region config defaults we would get different regions, + // update regions or create regions from scratch. const std::vector painting_extruders; for (auto it_print_object = m_objects.begin(); it_print_object != m_objects.end();) { // Find the range of PrintObjects sharing the same associated ModelObject. auto it_print_object_end = m_objects.begin(); PrintObject &print_object = *(*it_print_object); const ModelObject &model_object = *print_object.model_object(); - ModelObjectStatus &model_object_status = const_cast(model_object_status.reuse(model_object)); + ModelObjectStatus &model_object_status = const_cast(model_object_status_db.reuse(model_object)); PrintObjectRegions *print_object_regions = model_object_status.print_object_regions; for (++ it_print_object_end; it_print_object != m_objects.end() && (*it_print_object)->model_object() == (*it_print_object_end)->model_object(); ++ it_print_object_end) assert((*it_print_object_end)->m_shared_regions == nullptr || (*it_print_object_end)->m_shared_regions == print_object_regions); if (print_object_regions == nullptr) print_object_regions = new PrintObjectRegions {}; if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Valid) { - if (verify_update_print_object_regions( + // Verify that the trafo for regions & volume bounding boxes thus for regions is still applicable. + if (print_object_regions && ! trafos_differ_in_rotation_and_mirroring_by_z_only(print_object_regions->trafo_bboxes, model_object_status.print_instances.front().trafo)) + print_object_regions->clear(); + if (print_object_regions && + verify_update_print_object_regions( print_object.model_object()->volumes, m_default_region_config, num_extruders, @@ -1237,44 +1265,38 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(print_object->invalidate_state_by_config_options(region.config(), region_config, diff)); region.config_apply_only(region_config, diff, false); })) { - // Regions are valid, assign them to the other PrintObjects. - for (auto it = it_print_object; it != it_print_object_end; ++ it) - if ((*it)->m_shared_regions == nullptr) { - (*it)->m_shared_regions = print_object_regions; - print_object_regions->ref_cnt_inc(); - } + // Regions are valid, just keep them. } else { // Regions were reshuffled. for (auto it = it_print_object; it != it_print_object_end; ++ it) if ((*it)->m_shared_regions != nullptr) { assert(print_object_regions == (*it)->m_shared_regions); update_apply_status((*it)->invalidate_all_steps()); - (*it)->m_shared_regions = nullptr; - print_object_regions->ref_cnt_dec(); } // At least reuse layer ranges and bounding boxes of ModelVolumes. model_object_status.print_object_regions_status = ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid; print_regions_reshuffled = true; } } - if (model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::PartiallyValid) { - // Reuse layer ranges and bounding boxes of ModelVolumes. - } else { - assert(model_object_status.print_object_regions_status == ModelObjectStatus::PrintObjectRegionsStatus::Invalid); - // Layer ranges with their associated configurations. - LayerRanges layer_ranges; - layer_ranges.assign(print_object.model_object()->layer_config_ranges); - print_object_regions = new PrintObjectRegions(generate_print_object_regions( + if (print_object_regions == nullptr || model_object_status.print_object_regions_status != ModelObjectStatus::PrintObjectRegionsStatus::Valid) { + // Layer ranges with their associated configurations. Remove overlaps between the ranges + // and create the regions from scratch. + print_object_regions = generate_print_object_regions( + print_object_regions, print_object.model_object()->volumes, - layer_ranges, + LayerRanges(print_object.model_object()->layer_config_ranges), + model_object_status.print_instances.front().trafo, m_default_region_config, num_extruders, - painting_extruders)); - for (auto it = it_print_object; it != it_print_object_end; ++ it) { + painting_extruders); + } + for (auto it = it_print_object; it != it_print_object_end; ++it) + if ((*it)->m_shared_regions) + assert((*it)->m_shared_regions == print_object_regions); + else { (*it)->m_shared_regions = print_object_regions; print_object_regions->ref_cnt_inc(); } - } it_print_object = it_print_object_end; } @@ -1284,7 +1306,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ std::set region_set; m_print_regions.clear(); PrintObjectRegions *print_object_regions = nullptr; - for (PrintObject *print_object : m_objects) { + for (PrintObject *print_object : m_objects) if (print_object_regions != print_object->m_shared_regions) { print_object_regions = print_object->m_shared_regions; for (std::unique_ptr &print_region : print_object_regions->all_regions) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 20c45f3cb..1f5246fce 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -101,58 +101,12 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances) std::vector> PrintObject::all_regions() const { std::vector> out; - out.reserve(m_all_regions.size()); - for (size_t i = 0; i < m_all_regions.size(); ++ i) - out.emplace_back(*m_all_regions[i]); + out.reserve(m_shared_regions->all_regions.size()); + for (const std::unique_ptr ®ion : m_shared_regions->all_regions) + out.emplace_back(*region.get()); return out; } -// Called by make_perimeters() -// 1) Decides Z positions of the layers, -// 2) Initializes layers and their regions -// 3) Slices the object meshes -// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes -// 5) Applies size compensation (offsets the slices in XY plane) -// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer -// Resulting expolygons of layer regions are marked as Internal. -void PrintObject::slice() -{ - if (! this->set_started(posSlice)) - return; - m_print->set_status(10, L("Processing triangulated mesh")); - std::vector layer_height_profile; - this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile); - m_print->throw_if_canceled(); - this->_slice(layer_height_profile); - m_print->throw_if_canceled(); - // Fix the model. - //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. - std::string warning = this->_fix_slicing_errors(); - m_print->throw_if_canceled(); - if (! warning.empty()) - BOOST_LOG_TRIVIAL(info) << warning; - // Simplify slices if required. - if (m_print->config().resolution) - this->simplify_slices(scale_(this->print()->config().resolution)); - // Update bounding boxes, back up raw slices of complex models. - tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), - [this](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - m_print->throw_if_canceled(); - Layer &layer = *m_layers[layer_idx]; - layer.lslices_bboxes.clear(); - layer.lslices_bboxes.reserve(layer.lslices.size()); - for (const ExPolygon &expoly : layer.lslices) - layer.lslices_bboxes.emplace_back(get_extents(expoly)); - layer.backup_untyped_slices(); - } - }); - if (m_layers.empty()) - throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"); - this->set_done(posSlice); -} - // 1) Merges typed region slices into stInternal type. // 2) Increases an "extra perimeters" counter at region slices where needed. // 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal). @@ -194,8 +148,8 @@ void PrintObject::make_perimeters() [this, ®ion, region_id](const tbb::blocked_range& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); - LayerRegion &layerm = *m_layers[layer_idx]->m_regions[region_id]; - const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->m_regions[region_id]; + LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id); + const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->get_region(region_id); const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices.surfaces); // Filter upper layer polygons in intersection_ppl by their bounding boxes? // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; @@ -747,7 +701,6 @@ bool PrintObject::invalidate_all_steps() bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps(); // Then reset some of the depending values. m_slicing_params.valid = false; - m_region_volumes.clear(); return result; } @@ -1605,7 +1558,7 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr } } -PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders) +PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders) { PrintRegionConfig config = default_region_config; apply_to_print_region_config(config, volume.get_object()->config.get()); @@ -1725,681 +1678,6 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c return updated; } -// 1) Decides Z positions of the layers, -// 2) Initializes layers and their regions -// 3) Slices the object meshes -// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes -// 5) Applies size compensation (offsets the slices in XY plane) -// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer -// Resulting expolygons of layer regions are marked as Internal. -// -// this should be idempotent -void PrintObject::_slice(const std::vector &layer_height_profile) -{ - BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info(); - - m_typed_slices = false; - - // 1) Initialize layers and their slice heights. - std::vector slice_zs; - { - this->clear_layers(); - // Object layers (pairs of bottom/top Z coordinate), without the raft. - std::vector object_layers = generate_object_layers(m_slicing_params, layer_height_profile); - // Reserve object layers for the raft. Last layer of the raft is the contact layer. - int id = int(m_slicing_params.raft_layers()); - slice_zs.reserve(object_layers.size()); - Layer *prev = nullptr; - for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) { - coordf_t lo = object_layers[i_layer]; - coordf_t hi = object_layers[i_layer + 1]; - coordf_t slice_z = 0.5 * (lo + hi); - Layer *layer = this->add_layer(id ++, hi - lo, hi + m_slicing_params.object_print_z_min, slice_z); - slice_zs.push_back(float(slice_z)); - if (prev != nullptr) { - prev->upper_layer = layer; - layer->lower_layer = prev; - } - // Make sure all layers contain layer region objects for all regions. - for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) - layer->add_region(&this->printing_region(region_id)); - prev = layer; - } - } - - // Count model parts and modifier meshes, check whether the model parts are of the same region. - int all_volumes_single_region = -2; // not set yet - bool has_z_ranges = false; - size_t num_volumes = 0; - size_t num_modifiers = 0; - for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) { - int last_volume_id = -1; - for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) { - const ModelVolume *model_volume = this->model_object()->volumes[volume_w_zrange.volume_idx]; - if (model_volume->is_model_part()) { - if (last_volume_id == volume_w_zrange.volume_idx) { - has_z_ranges = true; - } else { - last_volume_id = volume_w_zrange.volume_idx; - if (all_volumes_single_region == -2) - // first model volume met - all_volumes_single_region = region_id; - else if (all_volumes_single_region != region_id) - // multiple volumes met and they are not equal - all_volumes_single_region = -1; - ++ num_volumes; - } - } else if (model_volume->is_modifier()) - ++ num_modifiers; - } - } - assert(num_volumes > 0); - - // Slice all non-modifier volumes. - bool clipped = false; - bool upscaled = false; - bool spiral_vase = this->print()->config().spiral_vase; - auto slicing_mode = spiral_vase ? MeshSlicingParams::SlicingMode::PositiveLargestContour : MeshSlicingParams::SlicingMode::Regular; - if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) { - // Cheap path: Slice regions without mutual clipping. - // The cheap path is possible if no clipping is allowed or if slicing volumes of just a single region. - for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; - // slicing in parallel - size_t slicing_mode_normal_below_layer = 0; - if (spiral_vase) { - // Slice the bottom layers with SlicingMode::Regular. - // This needs to be in sync with LayerRegion::make_perimeters() spiral_vase! - const PrintRegionConfig &config = this->printing_region(region_id).config(); - slicing_mode_normal_below_layer = size_t(config.bottom_solid_layers.value); - for (; slicing_mode_normal_below_layer < slice_zs.size() && slice_zs[slicing_mode_normal_below_layer] < config.bottom_solid_min_thickness - EPSILON; - ++ slicing_mode_normal_below_layer); - } - std::vector expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode, slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode::Regular); - m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; - for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) - m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal); - m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end"; - } - } else { - // Expensive path: Slice one volume after the other in the order they are presented at the user interface, - // clip the last volumes with the first. - // First slice the volumes. - struct SlicedVolume { - SlicedVolume(int volume_id, int region_id, std::vector &&expolygons_by_layer) : - volume_id(volume_id), region_id(region_id), expolygons_by_layer(std::move(expolygons_by_layer)) {} - int volume_id; - int region_id; - std::vector expolygons_by_layer; - }; - std::vector sliced_volumes; - sliced_volumes.reserve(num_volumes); - for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { - const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id]; - for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) { - int volume_id = volumes_and_ranges.volumes[i].volume_idx; - const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; - if (model_volume->is_model_part()) { - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id; - // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume. - std::vector ranges; - ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range); - size_t j = i + 1; - for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) - if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON) - ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second; - else - ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range); - // slicing in parallel - sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume)); - i = j; - } else - ++ i; - } - } - // Second clip the volumes in the order they are presented at the user interface. - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start"; - tbb::parallel_for( - tbb::blocked_range(0, slice_zs.size()), - [this, &sliced_volumes, num_modifiers](const tbb::blocked_range& range) { - float delta = float(scale_(m_config.xy_size_compensation.value)); - // Only upscale together with clipping if there are no modifiers, as the modifiers shall be applied before upscaling - // (upscaling may grow the object outside of the modifier mesh). - bool upscale = delta > 0 && num_modifiers == 0; - for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - m_print->throw_if_canceled(); - // Trim volumes in a single layer, one by the other, possibly apply upscaling. - { - Polygons processed; - for (SlicedVolume &sliced_volume : sliced_volumes) - if (! sliced_volume.expolygons_by_layer.empty()) { - ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]); - if (upscale) - slices = offset_ex(std::move(slices), delta); - if (! processed.empty()) - // Trim by the slices of already processed regions. - slices = diff_ex(slices, processed); - if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size()) - // Collect the already processed regions to trim the to be processed regions. - polygons_append(processed, slices); - sliced_volume.expolygons_by_layer[layer_id] = std::move(slices); - } - } - // Collect and union volumes of a single region. - for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) { - ExPolygons expolygons; - size_t num_volumes = 0; - for (SlicedVolume &sliced_volume : sliced_volumes) - if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer.empty() && ! sliced_volume.expolygons_by_layer[layer_id].empty()) { - ++ num_volumes; - append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id])); - } - if (num_volumes > 1) - // Merge the islands using a positive / negative offset. - expolygons = offset_ex(offset_ex(expolygons, float(scale_(EPSILON))), -float(scale_(EPSILON))); - m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons), stInternal); - } - } - }); - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - end"; - clipped = true; - upscaled = m_config.xy_size_compensation.value > 0 && num_modifiers == 0; - } - - // Slice all modifier volumes. - if (m_region_volumes.size() > 1) { - for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { - BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id; - // slicing in parallel - std::vector expolygons_by_layer = this->slice_modifiers(region_id, slice_zs); - m_print->throw_if_canceled(); - if (expolygons_by_layer.empty()) - continue; - // loop through the other regions and 'steal' the slices belonging to this one - BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start"; - tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), - [this, &expolygons_by_layer, region_id](const tbb::blocked_range& range) { - for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - for (size_t other_region_id = 0; other_region_id < m_region_volumes.size(); ++ other_region_id) { - if (region_id == other_region_id) - continue; - Layer *layer = m_layers[layer_id]; - LayerRegion *layerm = layer->m_regions[region_id]; - LayerRegion *other_layerm = layer->m_regions[other_region_id]; - if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty()) - continue; - ExPolygons my_parts = intersection_ex(other_layerm->slices.surfaces, expolygons_by_layer[layer_id]); - if (my_parts.empty()) - continue; - // Remove such parts from original region. - other_layerm->slices.set(diff_ex(other_layerm->slices.surfaces, my_parts), stInternal); - // Append new parts to our region. - layerm->slices.append(std::move(my_parts), stInternal); - } - } - }); - m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end"; - } - } - - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers"; - while (! m_layers.empty()) { - const Layer *layer = m_layers.back(); - if (! layer->empty()) - goto end; - delete layer; - m_layers.pop_back(); - if (! m_layers.empty()) - m_layers.back()->upper_layer = nullptr; - } - m_print->throw_if_canceled(); -end: - ; - - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin"; - { - // Compensation value, scaled. - const float xy_compensation_scaled = float(scale_(m_config.xy_size_compensation.value)); - const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ? - // Only enable Elephant foot compensation if printing directly on the print bed. - float(scale_(m_config.elefant_foot_compensation.value)) : - 0.f; - // Uncompensated slices for the first layer in case the Elephant foot compensation is applied. - ExPolygons lslices_1st_layer; - tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), - [this, upscaled, clipped, xy_compensation_scaled, elephant_foot_compensation_scaled, &lslices_1st_layer] - (const tbb::blocked_range& range) { - for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { - m_print->throw_if_canceled(); - Layer *layer = m_layers[layer_id]; - // Apply size compensation and perform clipping of multi-part objects. - float elfoot = (layer_id == 0) ? elephant_foot_compensation_scaled : 0.f; - if (layer->m_regions.size() == 1) { - assert(! upscaled); - assert(! clipped); - // Optimized version for a single region layer. - // Single region, growing or shrinking. - LayerRegion *layerm = layer->m_regions.front(); - if (elfoot > 0) { - // Apply the elephant foot compensation and store the 1st layer slices without the Elephant foot compensation applied. - lslices_1st_layer = to_expolygons(std::move(layerm->slices.surfaces)); - float delta = xy_compensation_scaled; - if (delta > elfoot) { - delta -= elfoot; - elfoot = 0.f; - } else if (delta > 0) - elfoot -= delta; - layerm->slices.set( - union_ex( - Slic3r::elephant_foot_compensation( - (delta == 0.f) ? lslices_1st_layer : offset_ex(lslices_1st_layer, delta), - layerm->flow(frExternalPerimeter), unscale(elfoot))), - stInternal); - if (xy_compensation_scaled != 0.f) - lslices_1st_layer = offset_ex(std::move(lslices_1st_layer), xy_compensation_scaled); - } else if (xy_compensation_scaled != 0.f) { - // Apply the XY compensation. - layerm->slices.set( - offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), xy_compensation_scaled), - stInternal); - } - } else { - bool upscale = ! upscaled && xy_compensation_scaled > 0.f; - bool clip = ! clipped && m_config.clip_multipart_objects.value; - if (upscale || clip) { - // Multiple regions, growing or just clipping one region by the other. - // When clipping the regions, priority is given to the first regions. - Polygons processed; - for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { - LayerRegion *layerm = layer->m_regions[region_id]; - ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces)); - if (upscale) - slices = offset_ex(std::move(slices), xy_compensation_scaled); - if (region_id > 0 && clip) - // Trim by the slices of already processed regions. - slices = diff_ex(slices, processed); - if (clip && (region_id + 1 < layer->m_regions.size())) - // Collect the already processed regions to trim the to be processed regions. - polygons_append(processed, slices); - layerm->slices.set(std::move(slices), stInternal); - } - } - if (xy_compensation_scaled < 0.f || elfoot > 0.f) { - // Apply the negative XY compensation. - Polygons trimming; - static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5); - if (elfoot > 0.f) { - lslices_1st_layer = offset_ex(layer->merged(eps), std::min(xy_compensation_scaled, 0.f) - eps); - trimming = to_polygons(Slic3r::elephant_foot_compensation(lslices_1st_layer, - layer->m_regions.front()->flow(frExternalPerimeter), unscale(elfoot))); - } else - trimming = offset(layer->merged(float(SCALED_EPSILON)), xy_compensation_scaled - float(SCALED_EPSILON)); - for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) - layer->m_regions[region_id]->trim_surfaces(trimming); - } - } - // Merge all regions' slices to get islands, chain them by a shortest path. - layer->make_slices(); - } - }); - if (elephant_foot_compensation_scaled > 0.f && ! m_layers.empty()) { - // The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value. - // Store the uncompensated value there. - assert(m_layers.front()->id() == 0); - m_layers.front()->lslices = std::move(lslices_1st_layer); - } - } - - m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - end"; -} - -// To be used only if there are no layer span specific configurations applied, which would lead to z ranges being generated for this region. -std::vector PrintObject::slice_region(size_t region_id, const std::vector &z, MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below) const -{ - std::vector volumes; - if (region_id < m_region_volumes.size()) { - for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) { - const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx]; - if (volume->is_model_part()) - volumes.emplace_back(volume); - } - } - return this->slice_volumes(z, mode, slicing_mode_normal_below_layer, mode_below, volumes); -} - -// Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_w_zrange at most once. -std::vector PrintObject::slice_modifiers(size_t region_id, const std::vector &slice_zs) const -{ - std::vector out; - if (region_id < m_region_volumes.size()) - { - std::vector> volume_ranges; - const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id]; - volume_ranges.reserve(volumes_and_ranges.volumes.size()); - for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) { - int volume_id = volumes_and_ranges.volumes[i].volume_idx; - const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; - if (model_volume->is_modifier()) { - std::vector ranges; - ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range); - size_t j = i + 1; - for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) { - if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON) - ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second; - else - ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range); - } - volume_ranges.emplace_back(std::move(ranges)); - i = j; - } else - ++ i; - } - - if (! volume_ranges.empty()) - { - bool equal_ranges = true; - for (size_t i = 1; i < volume_ranges.size(); ++ i) { - assert(! volume_ranges[i].empty()); - if (volume_ranges.front() != volume_ranges[i]) { - equal_ranges = false; - break; - } - } - - if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) { - // No modifier in this region was split to layer spans. - std::vector volumes; - for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) { - const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx]; - if (volume->is_modifier()) - volumes.emplace_back(volume); - } - out = this->slice_volumes(slice_zs, MeshSlicingParams::SlicingMode::Regular, volumes); - } else { - // Some modifier in this region was split to layer spans. - std::vector merge; - for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) { - const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id]; - for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) { - int volume_id = volumes_and_ranges.volumes[i].volume_idx; - const ModelVolume *model_volume = this->model_object()->volumes[volume_id]; - if (model_volume->is_modifier()) { - BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id; - // Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume. - std::vector ranges; - ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range); - size_t j = i + 1; - for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) - ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range); - // slicing in parallel - std::vector this_slices = this->slice_volume(slice_zs, ranges, MeshSlicingParams::SlicingMode::Regular, *model_volume); - // Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume. - if (out.empty()) { - out = std::move(this_slices); - merge.assign(out.size(), false); - } else if (!this_slices.empty()) { - assert(out.size() == this_slices.size()); - for (size_t i = 0; i < out.size(); ++ i) - if (! this_slices[i].empty()) { - if (! out[i].empty()) { - append(out[i], this_slices[i]); - merge[i] = true; - } else - out[i] = std::move(this_slices[i]); - } - } - i = j; - } else - ++ i; - } - } - for (size_t i = 0; i < merge.size(); ++ i) - if (merge[i]) - out[i] = union_ex(out[i]); - } - } - } - - return out; -} - -std::vector PrintObject::slice_support_volumes(const ModelVolumeType &model_volume_type) const -{ - std::vector volumes; - for (const ModelVolume *volume : this->model_object()->volumes) - if (volume->type() == model_volume_type) - volumes.emplace_back(volume); - std::vector zs; - zs.reserve(this->layers().size()); - for (const Layer *l : this->layers()) - zs.emplace_back((float)l->slice_z); - return this->slice_volumes(zs, MeshSlicingParams::SlicingMode::Regular, volumes); -} - -//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it. -static void fix_mesh_connectivity(TriangleMesh &mesh) -{ - auto nr_degenerated = mesh.stl.stats.degenerate_facets; - stl_check_facets_exact(&mesh.stl); - if (nr_degenerated != mesh.stl.stats.degenerate_facets) - // stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation. - stl_generate_shared_vertices(&mesh.stl, mesh.its); -} - -std::vector PrintObject::slice_volumes( - const std::vector &z, - MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below, - const std::vector &volumes) const -{ - std::vector layers; - if (! volumes.empty()) { - // Compose mesh. - //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. - TriangleMesh mesh(volumes.front()->mesh()); - mesh.transform(volumes.front()->get_matrix(), true); - assert(mesh.repaired); - if (volumes.size() == 1 && mesh.repaired) - fix_mesh_connectivity(mesh); - for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) { - const ModelVolume &model_volume = *volumes[idx_volume]; - TriangleMesh vol_mesh(model_volume.mesh()); - vol_mesh.transform(model_volume.get_matrix(), true); - mesh.merge(vol_mesh); - mesh.repair(false); - } - if (mesh.stl.stats.number_of_facets > 0) { - mesh.transform(m_trafo, true); - // apply XY shift - mesh.translate(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0); - // perform actual slicing - const Print *print = this->print(); - // TriangleMeshSlicer needs shared vertices, also this calls the repair() function. - mesh.require_shared_vertices(); - MeshSlicingParamsEx params { { mode, slicing_mode_normal_below_layer, mode_below }, float(m_config.slice_closing_radius.value) }; - layers = slice_mesh_ex(mesh.its, z, params, [print]() { print->throw_if_canceled(); }); - m_print->throw_if_canceled(); - } - } - return layers; -} - -std::vector PrintObject::slice_volume(const std::vector &z, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const -{ - std::vector layers; - if (! z.empty()) { - // Compose mesh. - //FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them. - TriangleMesh mesh(volume.mesh()); - mesh.transform(volume.get_matrix(), true); - if (mesh.repaired) - fix_mesh_connectivity(mesh); - if (mesh.stl.stats.number_of_facets > 0) { - mesh.transform(m_trafo, true); - // apply XY shift - mesh.translate(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0); - // perform actual slicing - const Print *print = this->print(); - // TriangleMeshSlicer needs the shared vertices. - mesh.require_shared_vertices(); - MeshSlicingParamsEx params; - params.mode = mode; - params.closing_radius = float(m_config.slice_closing_radius.value); - layers = slice_mesh_ex(mesh.its, z, params, [print](){ print->throw_if_canceled(); }); - m_print->throw_if_canceled(); - } - } - return layers; -} - -// Filter the zs not inside the ranges. The ranges are closed at the bottom and open at the top, they are sorted lexicographically and non overlapping. -std::vector PrintObject::slice_volume(const std::vector &z, const std::vector &ranges, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const -{ - std::vector out; - if (! z.empty() && ! ranges.empty()) { - if (ranges.size() == 1 && z.front() >= ranges.front().first && z.back() < ranges.front().second) { - // All layers fit into a single range. - out = this->slice_volume(z, mode, volume); - } else { - std::vector z_filtered; - std::vector> n_filtered; - z_filtered.reserve(z.size()); - n_filtered.reserve(2 * ranges.size()); - size_t i = 0; - for (const t_layer_height_range &range : ranges) { - for (; i < z.size() && z[i] < range.first; ++ i) ; - size_t first = i; - for (; i < z.size() && z[i] < range.second; ++ i) - z_filtered.emplace_back(z[i]); - if (i > first) - n_filtered.emplace_back(std::make_pair(first, i)); - } - if (! n_filtered.empty()) { - std::vector layers = this->slice_volume(z_filtered, mode, volume); - out.assign(z.size(), ExPolygons()); - i = 0; - for (const std::pair &span : n_filtered) - for (size_t j = span.first; j < span.second; ++ j) - out[j] = std::move(layers[i ++]); - } - } - } - return out; -} - -std::string PrintObject::_fix_slicing_errors() -{ - // Collect layers with slicing errors. - // These layers will be fixed in parallel. - std::vector buggy_layers; - buggy_layers.reserve(m_layers.size()); - for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) - if (m_layers[idx_layer]->slicing_errors) - buggy_layers.push_back(idx_layer); - - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - begin"; - tbb::parallel_for( - tbb::blocked_range(0, buggy_layers.size()), - [this, &buggy_layers](const tbb::blocked_range& range) { - for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) { - m_print->throw_if_canceled(); - size_t idx_layer = buggy_layers[buggy_layer_idx]; - Layer *layer = m_layers[idx_layer]; - assert(layer->slicing_errors); - // Try to repair the layer surfaces by merging all contours and all holes from neighbor layers. - // BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer; - for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { - LayerRegion *layerm = layer->m_regions[region_id]; - // Find the first valid layer below / above the current layer. - const Surfaces *upper_surfaces = nullptr; - const Surfaces *lower_surfaces = nullptr; - for (size_t j = idx_layer + 1; j < m_layers.size(); ++ j) - if (! m_layers[j]->slicing_errors) { - upper_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; - break; - } - for (int j = int(idx_layer) - 1; j >= 0; -- j) - if (! m_layers[j]->slicing_errors) { - lower_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; - break; - } - // Collect outer contours and holes from the valid layers above & below. - Polygons outer; - outer.reserve( - ((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) + - ((lower_surfaces == nullptr) ? 0 : lower_surfaces->size())); - size_t num_holes = 0; - if (upper_surfaces) - for (const auto &surface : *upper_surfaces) { - outer.push_back(surface.expolygon.contour); - num_holes += surface.expolygon.holes.size(); - } - if (lower_surfaces) - for (const auto &surface : *lower_surfaces) { - outer.push_back(surface.expolygon.contour); - num_holes += surface.expolygon.holes.size(); - } - Polygons holes; - holes.reserve(num_holes); - if (upper_surfaces) - for (const auto &surface : *upper_surfaces) - polygons_append(holes, surface.expolygon.holes); - if (lower_surfaces) - for (const auto &surface : *lower_surfaces) - polygons_append(holes, surface.expolygon.holes); - layerm->slices.set(diff_ex(union_(outer), holes), stInternal); - } - // Update layer slices after repairing the single regions. - layer->make_slices(); - } - }); - m_print->throw_if_canceled(); - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; - - // remove empty layers from bottom - while (! m_layers.empty() && (m_layers.front()->lslices.empty() || m_layers.front()->empty())) { - delete m_layers.front(); - m_layers.erase(m_layers.begin()); - m_layers.front()->lower_layer = nullptr; - for (size_t i = 0; i < m_layers.size(); ++ i) - m_layers[i]->set_id(m_layers[i]->id() - 1); - } - - return buggy_layers.empty() ? "" : - "The model has overlapping or self-intersecting facets. I tried to repair it, " - "however you might want to check the results or repair the input file and retry.\n"; -} - -// Simplify the sliced model, if "resolution" configuration parameter > 0. -// The simplification is problematic, because it simplifies the slices independent from each other, -// which makes the simplified discretization visible on the object surface. -void PrintObject::simplify_slices(double distance) -{ - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - begin"; - tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), - [this, distance](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - m_print->throw_if_canceled(); - Layer *layer = m_layers[layer_idx]; - for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx) - layer->m_regions[region_idx]->slices.simplify(distance); - { - ExPolygons simplified; - for (const ExPolygon &expoly : layer->lslices) - expoly.simplify(distance, &simplified); - layer->lslices = std::move(simplified); - } - } - }); - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end"; -} - // Only active if config->infill_only_where_needed. This step trims the sparse infill, // so it acts as an internal support. It maintains all other infill types intact. // Here the internal surfaces and perimeters have to be supported by the sparse infill. diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index f96cebed3..961eaefb0 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -1,5 +1,7 @@ -#include "Print.hpp" +#include "ElephantFootCompensation.hpp" #include "I18N.hpp" +#include "Layer.hpp" +#include "Print.hpp" #include @@ -10,22 +12,21 @@ namespace Slic3r { -static inline LayerPtrs new_layers( +LayerPtrs new_layers( PrintObject *print_object, // Object layers (pairs of bottom/top Z coordinate), without the raft. - const std::vector &object_layers, - // Reserve object layers for the raft. Last layer of the raft is the contact layer. - size_t first_layer_id) + const std::vector &object_layers) { LayerPtrs out; out.reserve(object_layers.size()); - auto id = int(first_layer_id); - Layer *prev = nullptr; + auto id = int(print_object->slicing_parameters().raft_layers()); + coordf_t zmin = print_object->slicing_parameters().object_print_z_min; + Layer *prev = nullptr; for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) { coordf_t lo = object_layers[i_layer]; coordf_t hi = object_layers[i_layer + 1]; coordf_t slice_z = 0.5 * (lo + hi); - Layer *layer = new Layer(id ++, print_object, hi - lo, hi + m_slicing_params.object_print_z_min, slice_z); + Layer *layer = new Layer(id ++, print_object, hi - lo, hi + zmin, slice_z); out.emplace_back(layer); if (prev != nullptr) { prev->upper_layer = layer; @@ -36,7 +37,7 @@ static inline LayerPtrs new_layers( return out; } -template +template static inline std::vector zs_from_layers(const LayerContainer &layers) { std::vector zs; @@ -47,57 +48,38 @@ static inline std::vector zs_from_layers(const LayerContainer &layers) } //FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it. -static void fix_mesh_connectivity(TriangleMesh &mesh) +// This function will go away once we get rid of admesh from ModelVolume. +static indexed_triangle_set get_mesh_its_fix_mesh_connectivity(TriangleMesh mesh) { - auto nr_degenerated = mesh.stl.stats.degenerate_facets; - stl_check_facets_exact(&mesh.stl); - if (nr_degenerated != mesh.stl.stats.degenerate_facets) - // stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation. - stl_generate_shared_vertices(&mesh.stl, mesh.its); + assert(mesh.repaired && mesh.has_shared_vertices()); + if (mesh.stl.stats.number_of_facets > 0) { + assert(mesh.repaired && mesh.has_shared_vertices()); + auto nr_degenerated = mesh.stl.stats.degenerate_facets; + stl_check_facets_exact(&mesh.stl); + if (nr_degenerated != mesh.stl.stats.degenerate_facets) + // stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation. + stl_generate_shared_vertices(&mesh.stl, mesh.its); + } else + mesh.its.clear(); + return std::move(mesh.its); } -struct SliceVolumeParams -{ - const SlicingMode mode { SlicingMode::Regular }; - // For vase mode: below this layer a different slicing mode will be used to produce a single contour. - // 0 = ignore. - const size_t slicing_mode_normal_below_layer { 0 }; - // Mode to apply below slicing_mode_normal_below_layer. Ignored if slicing_mode_nromal_below_layer == 0. - const SlicingMode mode_below { SlicingMode::Regular }; - - // Morphological closing operation when creating output expolygons. - const float closing_radius { 0 }; - // Positive offset applied when creating output expolygons. - const float extra_offset { 0 }; - // Resolution for contour simplification. - // 0 = don't simplify. - const double resolution { 0 }; - - // Transformation of the object owning the ModelVolume. - Transform3d object_trafo; -}; - // Slice single triangle mesh. static std::vector slice_volume( - const ModelVolume &volume, - const std::vector &z, - const SliceVolumeParams ¶ms, - const TriangleMeshSlicer::throw_on_cancel_callback_type &throw_on_cancel_callback) + const ModelVolume &volume, + const std::vector &zs, + const MeshSlicingParamsEx ¶ms, + const std::function &throw_on_cancel_callback) { std::vector layers; - if (! z.empty()) { - TriangleMesh mesh(volume.mesh()); - mesh.transform(params.object_trafo * volume.get_matrix(), true); - if (mesh.repaired) - fix_mesh_connectivity(mesh); - if (mesh.stl.stats.number_of_facets > 0) { - // perform actual slicing - TriangleMeshSlicer mesh_slicer; - // TriangleMeshSlicer needs the shared vertices. - mesh.require_shared_vertices(); - mesh_slicer.init(&mesh, throw_on_cancel_callback); - //FIXME simplify contours - mesh_slicer.slice(z, mode, params.slicing_mode_normal_below_layer, params.mode_below, params.closing_radius, params.extra_offset, &layers, throw_on_cancel_callback); + if (! zs.empty()) { + indexed_triangle_set its = get_mesh_its_fix_mesh_connectivity(volume.mesh()); + if (its.indices.size() > 0) { + MeshSlicingParamsEx params2 { params }; + params2.trafo = params2.trafo * volume.get_matrix(); + if (params2.trafo.rotation().determinant() < 0.) + its_flip_triangles(its); + layers = slice_mesh_ex(its, zs, params, throw_on_cancel_callback); throw_on_cancel_callback(); } } @@ -107,11 +89,11 @@ static std::vector slice_volume( // Slice single triangle mesh. // Filter the zs not inside the ranges. The ranges are closed at the bottom and open at the top, they are sorted lexicographically and non overlapping. static std::vector slice_volume( - const ModelVolume &volume, - const std::vector &z, - const std::vector &ranges, - const SliceVolumeParams ¶ms, - const TriangleMeshSlicer::throw_on_cancel_callback_type &throw_on_cancel_callback) + const ModelVolume &volume, + const std::vector &z, + const std::vector &ranges, + const MeshSlicingParamsEx ¶ms, + const std::function &throw_on_cancel_callback) { std::vector out; if (! z.empty() && ! ranges.empty()) { @@ -161,14 +143,14 @@ static inline bool model_volume_needs_slicing(const ModelVolume &mv) // Apply closing radius. // Apply positive XY compensation to ModelVolumeType::MODEL_PART and ModelVolumeType::PARAMETER_MODIFIER, not to ModelVolumeType::NEGATIVE_VOLUME. // Apply contour simplification. -static std::vector slice_volumes( +static std::vector slice_volumes_inner( const PrintConfig &print_config, const PrintObjectConfig &print_object_config, const Transform3d &object_trafo, ModelVolumePtrs model_volumes, - const std::vector &layer_ranges; + const std::vector &layer_ranges, const std::vector &zs, - const TriangleMeshSlicer::throw_on_cancel_callback_type &throw_on_cancel_callback) + const std::function &throw_on_cancel_callback) { model_volumes_sort_by_id(model_volumes); @@ -179,32 +161,32 @@ static std::vector slice_volumes( if (layer_ranges.size() > 1) slicing_ranges.reserve(layer_ranges.size()); - SliceVolumeParams params_base; + MeshSlicingParamsEx params_base; params_base.closing_radius = float(print_object_config.slice_closing_radius.value); params_base.extra_offset = 0; - params_base.object_trafo = object_trafo; - params_base.resolution = print_config.resolution; + params_base.trafo = object_trafo; + params_base.resolution = scaled(print_config.resolution.value); const float extra_offset = print_object_config.xy_size_compensation > 0 ? float(print_object_config.xy_size_compensation.value) : 0.f; for (const ModelVolume *model_volume : model_volumes) if (model_volume_needs_slicing(*model_volume)) { - SliceVolumeParams params { params_base }; + MeshSlicingParamsEx params { params_base }; if (! model_volume->is_negative_volume()) - params.extra_offset = extra_ofset; + params.extra_offset = extra_offset; if (layer_ranges.size() == 1) { - if (const PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges.front(); layer_range.has_volume(model_volume->id())) { + if (const PrintObjectRegions::LayerRangeRegions &layer_range = layer_ranges.front(); layer_range.has_volume(model_volume->id())) { if (model_volume->is_model_part() && print_config.spiral_vase) { - auto it = std::find_first(layer_range.volume_regions.begin(), layer_range.volume_regions.end(), + auto it = std::find_if(layer_range.volume_regions.begin(), layer_range.volume_regions.end(), [model_volume](const auto &slice){ return model_volume == slice.model_volume; }); - params.mode = SlicingMode::PositiveLargestContour; + params.mode = MeshSlicingParams::SlicingMode::PositiveLargestContour; // Slice the bottom layers with SlicingMode::Regular. // This needs to be in sync with LayerRegion::make_perimeters() spiral_vase! - params.mode_below = SlicingMode::Regular; + params.mode_below = MeshSlicingParams::SlicingMode::Regular; const PrintRegionConfig ®ion_config = it->region->config(); - slicing_mode_normal_below_layer = size_t(region_config.bottom_solid_layers.value); - for (; slicing_mode_normal_below_layer < zs.size() && zs[slicing_mode_normal_below_layer] < region_config.bottom_solid_min_thickness - EPSILON; - ++ slicing_mode_normal_below_layer); + params.slicing_mode_normal_below_layer = size_t(region_config.bottom_solid_layers.value); + for (; params.slicing_mode_normal_below_layer < zs.size() && zs[params.slicing_mode_normal_below_layer] < region_config.bottom_solid_min_thickness - EPSILON; + ++ params.slicing_mode_normal_below_layer); } out.push_back({ model_volume->id(), @@ -212,7 +194,7 @@ static std::vector slice_volumes( }); } } else { - assert(! spiral_vase); + assert(! print_config.spiral_vase); slicing_ranges.clear(); for (const PrintObjectRegions::LayerRangeRegions &layer_range : layer_ranges) if (layer_range.has_volume(model_volume->id())) @@ -249,7 +231,7 @@ static std::vector> slices_to_regions( // If clipping is disabled, then ExPolygons produced by different volumes will never be merged, thus they will be allowed to overlap. // It is up to the model designer to handle these overlaps. const bool clip_multipart_objects, - const TriangleMeshSlicer::throw_on_cancel_callback_type &throw_on_cancel_callback) + const std::function &throw_on_cancel_callback) { model_volumes_sort_by_id(model_volumes); @@ -263,13 +245,13 @@ static std::vector> slices_to_regions( for (; z_idx < zs.size() && zs[z_idx] < layer_range.layer_height_range.first; ++ z_idx) ; if (layer_range.volume_regions.empty()) { } else if (layer_range.volume_regions.size() == 1) { - const ModelVolume *model_volume = layer_range.volume_regions.model_volume; + const ModelVolume *model_volume = layer_range.volume_regions.front().model_volume; assert(model_volume != nullptr); if (model_volume->is_model_part()) { VolumeSlices &slices_src = volume_slices_find_by_id(volume_slices, model_volume->id()); auto &slices_dst = slices_by_region[layer_range.volume_regions.front().region->print_object_region_id()]; for (; z_idx < zs.size() && zs[z_idx] < layer_range.layer_height_range.second; ++ z_idx) - slices_dst[z_idx] = std::move(slices_src[z_idx]); + slices_dst[z_idx] = std::move(slices_src.slices[z_idx]); } } else { zs_complex.reserve(zs.size()); @@ -279,14 +261,14 @@ static std::vector> slices_to_regions( bool complex = false; for (int idx_region = 0; idx_region < int(layer_range.volume_regions.size()); ++ idx_region) { const PrintObjectRegions::VolumeRegion ®ion = layer_range.volume_regions[idx_region]; - if (region.bbox.min().z() >= z && region.bbox.max().z() <= z) { + if (region.bbox->min.z() >= z && region.bbox->max.z() <= z) { if (idx_first_printable_region == -1 && region.model_volume->is_model_part()) idx_first_printable_region = idx_region; else if (idx_first_printable_region != -1) { // Test for overlap with some other region. for (int idx_region2 = idx_first_printable_region; idx_region2 < idx_region; ++ idx_region2) { const PrintObjectRegions::VolumeRegion ®ion2 = layer_range.volume_regions[idx_region2]; - if (region2.bbox.min().z() >= z && region2.bbox.max().z() <= z && overlap_in_xy(*region.bbox, *region2.bbox)) { + if (region2.bbox->min.z() >= z && region2.bbox->max.z() <= z && overlap_in_xy(*region.bbox, *region2.bbox)) { complex = true; break; } @@ -312,15 +294,15 @@ static std::vector> slices_to_regions( VolumeSlices* volume_slices; int prev_same_region { -1 }; }; - std::vector> layer_ranges_regions_to_slices(print_object_regions.layer_ranges.size(), std::vector()); + std::vector> layer_ranges_regions_to_slices(print_object_regions.layer_ranges.size(), std::vector()); std::vector last_volume_idx_of_region; - for (PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) { + for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges) { std::vector &layer_range_regions_to_slices = layer_ranges_regions_to_slices[&layer_range - print_object_regions.layer_ranges.data()]; layer_range_regions_to_slices.reserve(layer_range.volume_regions.size()); - last_volume_idx_of_region.assign(print_object_regions.layer_ranges.all_regions.size(), -1); - for (PrintObjectRegions::VolumeRegion ®ion : layer_range.volume_regions) { + last_volume_idx_of_region.assign(print_object_regions.all_regions.size(), -1); + for (const PrintObjectRegions::VolumeRegion ®ion : layer_range.volume_regions) { int region_id = region.region->print_object_region_id(); - layer_range_regions_to_slices.emplace_back({ &volume_slices_find_by_id(volume_slices, region.model_volume->id()), last_volume_idx_of_region[region_id] }); + layer_range_regions_to_slices.emplace_back(&volume_slices_find_by_id(volume_slices, region.model_volume->id()), last_volume_idx_of_region[region_id]); last_volume_idx_of_region[region_id] = ®ion - layer_range.volume_regions.data(); } } @@ -328,9 +310,9 @@ static std::vector> slices_to_regions( tbb::blocked_range(0, zs_complex.size()), [&slices_by_region, &model_volumes, &print_object_regions, &zs_complex, &layer_ranges_regions_to_slices, clip_multipart_objects, &throw_on_cancel_callback] (const tbb::blocked_range &range) { - float z = zs_complex[*range.begin()]; - it_layer_range = lower_bound_by_predicate(print_object_regions.layer_ranges.begin(), print_object_regions.layer_ranges.end(), - [z](const PrintObjectRegions::LayerRangeRegions &lr){ lr.layer_height_range.first < z; }); + float z = zs_complex[range.begin()]; + auto it_layer_range = lower_bound_by_predicate(print_object_regions.layer_ranges.begin(), print_object_regions.layer_ranges.end(), + [z](const PrintObjectRegions::LayerRangeRegions &lr){ return lr.layer_height_range.first < z; }); assert(it_layer_range != print_object_regions.layer_ranges.end() && it_layer_range->layer_height_range.first >= z && z < it_layer_range->layer_height_range.second); // Per volume_regions slices at this Z height. struct RegionSlice { @@ -351,13 +333,13 @@ static std::vector> slices_to_regions( assert(it_layer_range != print_object_regions.layer_ranges.end() && it_layer_range->layer_height_range.first >= z && z < it_layer_range->layer_height_range.second); const PrintObjectRegions::LayerRangeRegions &layer_range = *it_layer_range; { - SliceEntry &layer_range_regions_to_slices = layer_ranges_regions_to_slices[it_layer_range - print_object_regions.layer_ranges.begin()]; + std::vector &layer_range_regions_to_slices = layer_ranges_regions_to_slices[it_layer_range - print_object_regions.layer_ranges.begin()]; // Per volume_regions slices at thiz Z height. temp_slices.clear(); temp_slices.reserve(layer_range.volume_regions.size()); - for (VolumeSlices *slices : layer_range_regions_to_slices.volume_slices) { - const PrintRegion *region = layer_range.volume_regions[i].region; - temp_slices.push_back({ std::move(slices->slices[idx_z]), region ? region->print_object_region_id() : -1, slices->volume_id }); + for (SliceEntry &slices : layer_range_regions_to_slices) { + const PrintObjectRegions::VolumeRegion &volume_region = layer_range.volume_regions[&slices - layer_range_regions_to_slices.data()]; + temp_slices.push_back({ std::move(slices.volume_slices->slices[idx_z]), volume_region.region ? volume_region.region->print_object_region_id() : -1, volume_region.model_volume->id() }); } } for (int idx_region = 0; idx_region < int(layer_range.volume_regions.size()); ++ idx_region) @@ -365,16 +347,16 @@ static std::vector> slices_to_regions( const PrintObjectRegions::VolumeRegion ®ion = layer_range.volume_regions[idx_region]; if (region.model_volume->is_modifier()) { assert(region.parent > -1); - bool next_region_same_modifier = idx_region + 1 < temp_slices.size() && layer_range.volume_regions[idx_region + 1]->model_volume == region.model_volume; + bool next_region_same_modifier = idx_region + 1 < temp_slices.size() && layer_range.volume_regions[idx_region + 1].model_volume == region.model_volume; if (next_region_same_modifier) temp_slices[idx_region + 1] = std::move(temp_slices[idx_region]); - ExPolygons &parent_slice = temp_slices[region.parent]; - ExPolygons &this_slice = temp_slices[idx_region]; + RegionSlice &parent_slice = temp_slices[region.parent]; + RegionSlice &this_slice = temp_slices[idx_region]; if (parent_slice.empty()) - this_slice.clear(); + this_slice.expolygons.clear(); else { - ExPolygons &source_slice = temp_slices[idx_region + int(next_region_same_modifier)]; - this_slice = intersection_ex(parent_slice, source_slice); + RegionSlice &source_slice = temp_slices[idx_region + int(next_region_same_modifier)]; + this_slice.expolygons = intersection_ex(parent_slice.expolygons, source_slice.expolygons); } } else if ((region.model_volume->is_model_part() && clip_multipart_objects) || region.model_volume->is_negative_volume()) { // Clip every non-zero region preceding it. @@ -382,15 +364,14 @@ static std::vector> slices_to_regions( if (! temp_slices[idx_region2].empty()) { if (const PrintObjectRegions::VolumeRegion ®ion2 = layer_range.volume_regions[idx_region]; ! region2.model_volume->is_negative_volume() && overlap_in_xy(*region.bbox, *region2.bbox)) - temp_slices[idx_region] = diff_ex(temp_slices[idx_region], temp_slices[idx_region2]); + temp_slices[idx_region].expolygons = diff_ex(temp_slices[idx_region].expolygons, temp_slices[idx_region2].expolygons); } } } - } // Sort by region_id, push empty slices to the end. std::sort(temp_slices.begin(), temp_slices.end()); // Remove the empty slices. - temp_slices.erase(temp_slices.begin(), std::find_first(temp_slices.begin(), temp_slices.end(), [](const auto &slice){ return slice.empty(); })); + temp_slices.erase(std::find_if(temp_slices.begin(), temp_slices.end(), [](const auto &slice) { return slice.empty(); }), temp_slices.end()); // Merge slices and store them to the output. for (int i = 0; i < temp_slices.size();) { // Find a range of temp_slices with the same region_id. @@ -411,15 +392,15 @@ static std::vector> slices_to_regions( } if (merged) expolygons = offset_ex(offset_ex(expolygons, float(scale_(EPSILON))), -float(scale_(EPSILON))); - slices_by_region[temp_slices[i].region_id][z_idx] = std::move(expolygons); + slices_by_region[temp_slices[i].region_id][idx_z] = std::move(expolygons); i = j; } - + } }); - } } } +#if 0 // Z ranges are not applicable to modifier meshes, therefore a single volume will be found in volume_w_zrange at most once. std::vector PrintObject::slice_modifiers(size_t region_id, const std::vector &slice_zs) const { @@ -467,7 +448,7 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std if (volume->is_modifier()) volumes.emplace_back(volume); } - out = this->slice_volumes(slice_zs, SlicingMode::Regular, volumes); + out = this->slice_volumes(slice_zs, MeshSlicingParams::SlicingMode::Regular, volumes); } else { // Some modifier in this region was split to layer spans. std::vector merge; @@ -485,7 +466,7 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range); // slicing in parallel - std::vector this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume); + std::vector this_slices = this->slice_volume(slice_zs, ranges, MeshSlicingParams::SlicingMode::Regular, *model_volume); // Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume. if (out.empty()) { out = std::move(this_slices); @@ -515,41 +496,42 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std return out; } +#endif -std::string PrintObject::_fix_slicing_errors() +std::string fix_slicing_errors(LayerPtrs &layers, const std::function &throw_if_canceled) { // Collect layers with slicing errors. // These layers will be fixed in parallel. std::vector buggy_layers; - buggy_layers.reserve(m_layers.size()); - for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) - if (m_layers[idx_layer]->slicing_errors) + buggy_layers.reserve(layers.size()); + for (size_t idx_layer = 0; idx_layer < layers.size(); ++ idx_layer) + if (layers[idx_layer]->slicing_errors) buggy_layers.push_back(idx_layer); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - begin"; tbb::parallel_for( tbb::blocked_range(0, buggy_layers.size()), - [this, &buggy_layers](const tbb::blocked_range& range) { + [&layers, &throw_if_canceled, &buggy_layers](const tbb::blocked_range& range) { for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) { - m_print->throw_if_canceled(); + throw_if_canceled(); size_t idx_layer = buggy_layers[buggy_layer_idx]; - Layer *layer = m_layers[idx_layer]; + Layer *layer = layers[idx_layer]; assert(layer->slicing_errors); // Try to repair the layer surfaces by merging all contours and all holes from neighbor layers. // BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer; - for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) { - LayerRegion *layerm = layer->m_regions[region_id]; + for (size_t region_id = 0; region_id < layer->region_count(); ++ region_id) { + LayerRegion *layerm = layer->get_region(region_id); // Find the first valid layer below / above the current layer. const Surfaces *upper_surfaces = nullptr; const Surfaces *lower_surfaces = nullptr; - for (size_t j = idx_layer + 1; j < m_layers.size(); ++ j) - if (! m_layers[j]->slicing_errors) { - upper_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; + for (size_t j = idx_layer + 1; j < layers.size(); ++ j) + if (! layers[j]->slicing_errors) { + upper_surfaces = &layers[j]->regions()[region_id]->slices.surfaces; break; } for (int j = int(idx_layer) - 1; j >= 0; -- j) - if (! m_layers[j]->slicing_errors) { - lower_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces; + if (! layers[j]->slicing_errors) { + lower_surfaces = &layers[j]->regions()[region_id]->slices.surfaces; break; } // Collect outer contours and holes from the valid layers above & below. @@ -582,16 +564,16 @@ std::string PrintObject::_fix_slicing_errors() layer->make_slices(); } }); - m_print->throw_if_canceled(); + throw_if_canceled(); BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end"; // remove empty layers from bottom - while (! m_layers.empty() && (m_layers.front()->lslices.empty() || m_layers.front()->empty())) { - delete m_layers.front(); - m_layers.erase(m_layers.begin()); - m_layers.front()->lower_layer = nullptr; - for (size_t i = 0; i < m_layers.size(); ++ i) - m_layers[i]->set_id(m_layers[i]->id() - 1); + while (! layers.empty() && (layers.front()->lslices.empty() || layers.front()->empty())) { + delete layers.front(); + layers.erase(layers.begin()); + layers.front()->lower_layer = nullptr; + for (size_t i = 0; i < layers.size(); ++ i) + layers[i]->set_id(layers[i]->id() - 1); } return buggy_layers.empty() ? "" : @@ -599,31 +581,6 @@ std::string PrintObject::_fix_slicing_errors() "however you might want to check the results or repair the input file and retry.\n"; } -// Simplify the sliced model, if "resolution" configuration parameter > 0. -// The simplification is problematic, because it simplifies the slices independent from each other, -// which makes the simplified discretization visible on the object surface. -void PrintObject::simplify_slices(double distance) -{ - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - begin"; - tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), - [this, distance](const tbb::blocked_range& range) { - for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { - m_print->throw_if_canceled(); - Layer *layer = m_layers[layer_idx]; - for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx) - layer->m_regions[region_idx]->slices.simplify(distance); - { - ExPolygons simplified; - for (const ExPolygon &expoly : layer->lslices) - expoly.simplify(distance, &simplified); - layer->lslices = std::move(simplified); - } - } - }); - BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end"; -} - // Called by make_perimeters() // 1) Decides Z positions of the layers, // 2) Initializes layers and their regions @@ -642,12 +599,12 @@ void PrintObject::slice() m_print->throw_if_canceled(); m_typed_slices = false; this->clear_layers(); - m_layers = new_layers(this, generate_object_layers(m_slicing_params, layer_height_profile), m_slicing_params.raft_layers()); - this->_slice(); + m_layers = new_layers(this, generate_object_layers(m_slicing_params, layer_height_profile)); + this->slice_volumes(); m_print->throw_if_canceled(); // Fix the model. //FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend. - std::string warning = this->_fix_slicing_errors(); + std::string warning = fix_slicing_errors(m_layers, [this](){ m_print->throw_if_canceled(); }); m_print->throw_if_canceled(); if (! warning.empty()) BOOST_LOG_TRIVIAL(info) << warning; @@ -682,16 +639,17 @@ void PrintObject::slice() void PrintObject::slice_volumes() { BOOST_LOG_TRIVIAL(info) << "Slicing volumes..." << log_memory_info(); - - bool spiral_vase = this->print()->config().spiral_vase; - auto throw_on_cancel_callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); + const Print* print = this->print(); + const bool spiral_vase = print->config().spiral_vase; + const auto throw_on_cancel_callback = std::function([print](){ print->throw_if_canceled(); }); std::vector slice_zs = zs_from_layers(m_layers); - - std::vector> region_slices = slices_to_regions(this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, - slice_volumes( - this->print()->config(), this->config(), - this->model_object()->volumes, m_shared_regions->layer_ranges, m_center_offset, slice_zs, SlicingMode::Regular, spiral_vase, throw_on_cancel_callback), + Transform3d trafo = this->trafo(); + trafo.pretranslate(Vec3d(- unscale(m_center_offset.x()), - unscale(m_center_offset.y()), 0)); + std::vector> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs, + slice_volumes_inner( + print->config(), this->config(), trafo, + this->model_object()->volumes, m_shared_regions->layer_ranges, slice_zs, throw_on_cancel_callback), m_config.clip_multipart_objects, throw_on_cancel_callback); @@ -702,6 +660,7 @@ void PrintObject::slice_volumes() } region_slices.clear(); +#if 0 // Second clip the volumes in the order they are presented at the user interface. BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - parallel clipping - start"; tbb::parallel_for( @@ -750,7 +709,8 @@ void PrintObject::slice_volumes() clipped = true; upscaled = m_config.xy_size_compensation.value > 0 && num_modifiers == 0; } - +#endif + BOOST_LOG_TRIVIAL(debug) << "Slicing volumes - removing top empty layers"; while (! m_layers.empty()) { const Layer *layer = m_layers.back(); @@ -864,9 +824,9 @@ void PrintObject::slice_volumes() std::vector PrintObject::slice_support_volumes(const ModelVolumeType model_volume_type) const { - size_t it_volume = this->model_object()->volumes.begin(); - size_t it_volume_end = this->model_object()->volumes.end(); - for (; it_volume->type() != model_volume_type && it_volume != it_volume_end; ++ it_volume) ; + auto it_volume = this->model_object()->volumes.begin(); + auto it_volume_end = this->model_object()->volumes.end(); + for (; (*it_volume)->type() != model_volume_type && it_volume != it_volume_end; ++ it_volume) ; std::vector slices; if (it_volume != it_volume_end) { // Found at least a single support volume of model_volume_type. @@ -874,10 +834,10 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType std::vector merge_layers; bool merge = false; const Print *print = this->print(); - auto throw_on_cancel_callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();}); - for (; it_volume != it_volume_end; ++ it_volume; it_volume != it_volume_end) - if (it_volume->type() == model_volume_type) { - std::vector slices2 = slice_volume(*(*it_volume), zs, SlicingMode::Regular, throw_on_cancel_callback); + auto throw_on_cancel_callback = std::function([print](){ print->throw_if_canceled(); }); + for (; it_volume != it_volume_end; ++ it_volume) + if ((*it_volume)->type() == model_volume_type) { + std::vector slices2 = slice_volume(*(*it_volume), zs, MeshSlicingParamsEx{}, throw_on_cancel_callback); if (slices.empty()) slices = std::move(slices2); else if (! slices2.empty()) { @@ -896,7 +856,7 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType } if (merge) { std::vector to_merge; - to_merge.reserve(zs); + to_merge.reserve(zs.size()); for (size_t i = 0; i < zs.size(); ++ i) if (merge_layers[i]) to_merge.emplace_back(&slices[i]); @@ -904,7 +864,7 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType tbb::blocked_range(0, to_merge.size()), [&to_merge](const tbb::blocked_range &range) { for (size_t i = range.begin(); i < range.end(); ++ i) - to_merge[i] = union_ex(to_merge[i]); + *to_merge[i] = union_ex(*to_merge[i]); }); } } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 5e18e43ef..2eb8d67e1 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -842,6 +842,12 @@ int its_merge_vertices(indexed_triangle_set &its, bool shrink_to_fit) return num_erased; } +void its_flip_triangles(indexed_triangle_set &its) +{ + for (stl_triangle_vertex_indices &face : its.indices) + std::swap(face(1), face(2)); +} + int its_remove_degenerate_faces(indexed_triangle_set &its, bool shrink_to_fit) { int last = 0; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 29a42eab3..0871b63cf 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -99,6 +99,9 @@ std::vector> create_vertex_faces_index(const indexed_triangl std::vector create_face_neighbors_index(const indexed_triangle_set &its); std::vector create_face_neighbors_index(const indexed_triangle_set &its, std::function throw_on_cancel_callback); +// After applying a transformation with negative determinant, flip the faces to keep the transformed mesh volume positive. +void its_flip_triangles(indexed_triangle_set &its); + // Merge duplicate vertices, return number of vertices removed. // This function will happily create non-manifolds if more than two faces share the same vertex position // or more than two faces share the same edge position!