From 3cb2f5f58f952bc104260c84292411894f957552 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 2 Feb 2023 16:43:17 +0100 Subject: [PATCH] Fix of Layer::sort_perimeters_into_islands() Fixes #9466 #9511 --- src/libslic3r/Layer.cpp | 113 ++++++++++++++++++++++++++++------------ 1 file changed, 80 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 378352be7..36716dde9 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -556,6 +556,9 @@ void Layer::sort_perimeters_into_islands( // If the current layer consists of multiple regions, then the fill_expolygons above are split by the source LayerRegion surfaces. const std::vector &layer_region_ids) { + assert(perimeter_and_gapfill_ranges.size() == fill_expolygons_ranges.size()); + assert(! layer_region_ids.empty()); + LayerRegion &this_layer_region = *m_regions[region_id]; // Bounding boxes of fill_expolygons. @@ -564,7 +567,6 @@ void Layer::sort_perimeters_into_islands( for (const ExPolygon &expolygon : fill_expolygons) fill_expolygons_bboxes.emplace_back(get_extents(expolygon)); - // Take one sample point for each source slice, to be used to sort source slices into layer slices. // source slice index + its sample. std::vector> perimeter_slices_queue; @@ -619,7 +621,11 @@ void Layer::sort_perimeters_into_islands( // Map of source fill_expolygon into region and fill_expolygon of that region. // -1: not set - std::vector> map_expolygon_to_region_and_fill; + struct RegionWithFillIndex { + int region_id{ -1 }; + int fill_in_region_id{ -1 }; + }; + std::vector map_expolygon_to_region_and_fill; const bool has_multiple_regions = layer_region_ids.size() > 1; assert(has_multiple_regions || layer_region_ids.size() == 1); // assign fill_surfaces to each layer @@ -633,7 +639,7 @@ void Layer::sort_perimeters_into_islands( const BoundingBox &bbr = fill_expolygons_bboxes[rhs]; return bbl.min < bbr.min || (bbl.min == bbr.min && bbl.max < bbr.max); }); - map_expolygon_to_region_and_fill.assign(fill_expolygons.size(), std::make_pair(-1, -1)); + map_expolygon_to_region_and_fill.assign(fill_expolygons.size(), {}); for (uint32_t region_idx : layer_region_ids) { LayerRegion &l = *m_regions[region_idx]; l.m_fill_expolygons = intersection_ex(l.slices().surfaces, fill_expolygons); @@ -649,23 +655,80 @@ void Layer::sort_perimeters_into_islands( if (uint32_t fill_id = *it_bbox; fill_expolygons_bboxes[fill_id] == bbox) { // With a very high probability the two expolygons match exactly. Confirm that. if (expolygons_match(expolygon, fill_expolygons[fill_id])) { - std::pair &ref = map_expolygon_to_region_and_fill[fill_id]; + RegionWithFillIndex &ref = map_expolygon_to_region_and_fill[fill_id]; // Only one expolygon produced by intersection with LayerRegion surface may match an expolygon of fill_expolygons. - assert(ref.first == -1); - ref.first = region_idx; - ref.second = int(&expolygon - l.fill_expolygons().data()); + assert(ref.region_id == -1 && ref.fill_in_region_id == -1); + ref.region_id = region_idx; + ref.fill_in_region_id = int(&expolygon - l.fill_expolygons().data()); } } } } + // Check whether any island contains multiple fills that fall into the same region, but not they are not contiguous. + // If so, sort fills in that particular region so that fills of an island become contiguous. + // Index of a region to sort. + int sort_region_id = -1; + // Temporary vector of fills for reordering. + ExPolygons fills_temp; + // Vector of new positions of the above. + std::vector new_positions; + do { + sort_region_id = -1; + for (size_t source_slice_idx = 0; source_slice_idx < fill_expolygons_ranges.size(); ++ source_slice_idx) + if (ExPolygonRange fill_range = fill_expolygons_ranges[source_slice_idx]; fill_range.size() > 1) { + // More than one expolygon exists for a single island. Check whether they are contiguous inside a single LayerRegion::fill_expolygons() vector. + uint32_t fill_idx = *fill_range.begin(); + if (const int fill_regon_id = map_expolygon_to_region_and_fill[fill_idx].region_id; fill_regon_id != -1) { + int fill_in_region_id = map_expolygon_to_region_and_fill[fill_idx].fill_in_region_id; + bool needs_sorting = false; + for (++ fill_idx; fill_idx != *fill_range.end(); ++ fill_idx) { + if (const RegionWithFillIndex &ref = map_expolygon_to_region_and_fill[fill_idx]; ref.region_id != fill_regon_id) { + // This island has expolygons split among multiple regions. + needs_sorting = false; + break; + } else if (ref.fill_in_region_id != ++ fill_in_region_id) { + // This island has all expolygons stored inside the same region, but not sorted. + needs_sorting = true; + } + } + if (needs_sorting) { + sort_region_id = fill_regon_id; + break; + } + } + } + if (sort_region_id != -1) { + // Reorder fills in region with sort_region index. + LayerRegion &layerm = *m_regions[sort_region_id]; + new_positions.assign(layerm.fill_expolygons().size(), -1); + int last = 0; + for (RegionWithFillIndex &ref : map_expolygon_to_region_and_fill) + if (ref.region_id == sort_region_id) { + new_positions[ref.fill_in_region_id] = last; + ref.fill_in_region_id = last ++; + } + for (auto &new_pos : new_positions) + if (new_pos == -1) + // Not referenced by any map_expolygon_to_region_and_fill. + new_pos = last ++; + // Move just the content of m_fill_expolygons to fills_temp, but don't move the container vector. + auto &fills = layerm.m_fill_expolygons; + assert(last == int(fills.size())); + fills_temp.reserve(fills.size()); + fills_temp.insert(fills_temp.end(), std::make_move_iterator(fills.begin()), std::make_move_iterator(fills.end())); + for (ExPolygon &ex : fills) + ex.clear(); + // Move / reoder the expolygons back into m_fill_expolygons. + for (size_t old_pos = 0; old_pos < new_positions.size(); ++ old_pos) + fills[new_positions[old_pos]] = std::move(fills_temp[old_pos]); + } + } while (sort_region_id != -1); } else { this_layer_region.m_fill_expolygons = std::move(fill_expolygons); this_layer_region.m_fill_expolygons_bboxes = std::move(fill_expolygons_bboxes); } } - // Sort perimeter extrusions, thin fill extrusions and fill expolygons into islands. - std::vector region_fill_sorted_last; auto insert_into_island = [ // Region where the perimeters, gap fills and fill expolygons are stored. region_id, @@ -678,10 +741,7 @@ void Layer::sort_perimeters_into_islands( // Mapping of fill_expolygon to region and its infill. &map_expolygon_to_region_and_fill, // Output - ®ions = m_regions, &lslices_ex = this->lslices_ex, - // fill_expolygons and fill_expolygons_bboxes need to be sorted into contiguous sequence by island, - // thus region_fill_sorted_last contains last fill_expolygon processed (meaning sorted). - ®ion_fill_sorted_last] + ®ions = m_regions, &lslices_ex = this->lslices_ex] (int lslice_idx, int source_slice_idx) { lslices_ex[lslice_idx].islands.push_back({}); LayerIsland &island = lslices_ex[lslice_idx].islands.back(); @@ -692,12 +752,12 @@ void Layer::sort_perimeters_into_islands( // Check whether the fill expolygons of this island were split into multiple regions. island.fill_region_id = LayerIsland::fill_region_composite_id; for (uint32_t fill_idx : fill_range) { - const std::pair &kvp = map_expolygon_to_region_and_fill[fill_idx]; - if (kvp.first == -1 || (island.fill_region_id != -1 && island.fill_region_id != kvp.second)) { + if (const int fill_regon_id = map_expolygon_to_region_and_fill[fill_idx].region_id; + fill_regon_id == -1 || (island.fill_region_id != LayerIsland::fill_region_composite_id && island.fill_region_id != fill_regon_id)) { island.fill_region_id = LayerIsland::fill_region_composite_id; break; } else - island.fill_region_id = kvp.second; + island.fill_region_id = fill_regon_id; } if (island.fill_expolygons_composite()) { // They were split, thus store the unsplit "composite" expolygons into the region of perimeters. @@ -709,23 +769,10 @@ void Layer::sort_perimeters_into_islands( fill_expolygons_bboxes.begin() + *fill_range.begin(), fill_expolygons_bboxes.begin() + *fill_range.end()); island.fill_expolygons = ExPolygonRange(begin, uint32_t(this_layer_region.fill_expolygons_composite().size())); } else { - if (region_fill_sorted_last.empty()) - region_fill_sorted_last.assign(regions.size(), 0); - uint32_t &last = region_fill_sorted_last[island.fill_region_id]; - // They were not split and they belong to the same region. - // Sort the region m_fill_expolygons to a continuous span. - uint32_t begin = last; - LayerRegion &layerm = *regions[island.fill_region_id]; - for (uint32_t fill_id : fill_range) { - uint32_t region_fill_id = map_expolygon_to_region_and_fill[fill_id].second; - assert(region_fill_id >= last); - if (region_fill_id > last) { - std::swap(layerm.m_fill_expolygons[region_fill_id], layerm.m_fill_expolygons[last]); - std::swap(layerm.m_fill_expolygons_bboxes[region_fill_id], layerm.m_fill_expolygons_bboxes[last]); - } - ++ last; - } - island.fill_expolygons = ExPolygonRange(begin, last); + // All expolygons are stored inside a single LayerRegion in a contiguous range. + island.fill_expolygons = ExPolygonRange( + map_expolygon_to_region_and_fill[*fill_range.begin()].fill_in_region_id, + map_expolygon_to_region_and_fill[*fill_range.end() - 1].fill_in_region_id + 1); } } else { // Layer island is made of one fill region only.