From 364300055e635a9d11b25c57aaa2889b34e14685 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 9 Dec 2020 14:07:22 +0100 Subject: [PATCH] Fix of spiral vase mode with holes in the bottom: Holes in the bottom layers (non-spiral vase layers) were being closed. Fixes Bad slicing in vase mode (unexpected bridge and solid infill layers) #3326 Fixes Model with holes in the base does not slice properly in "Vase Mode" #5359 --- src/libslic3r/LayerRegion.cpp | 14 +++++++++--- src/libslic3r/PerimeterGenerator.cpp | 4 ++-- src/libslic3r/PerimeterGenerator.hpp | 3 +++ src/libslic3r/Print.hpp | 11 ++++++++-- src/libslic3r/PrintObject.cpp | 25 ++++++++++++++++------ src/libslic3r/TriangleMesh.cpp | 32 ++++++++++++++++++---------- src/libslic3r/TriangleMesh.hpp | 13 +++++++++-- xs/xsp/PerimeterGenerator.xsp | 1 + 8 files changed, 77 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 5fda69f77..5ce56896d 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -54,15 +54,23 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec { this->perimeters.clear(); this->thin_fills.clear(); - + + const PrintConfig &print_config = this->layer()->object()->print()->config(); + const PrintRegionConfig ®ion_config = this->region()->config(); + // This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer! + bool spiral_vase = print_config.spiral_vase && + (this->layer()->id() >= region_config.bottom_solid_layers.value && + this->layer()->print_z >= region_config.bottom_solid_min_thickness - EPSILON); + PerimeterGenerator g( // input: &slices, this->layer()->height, this->flow(frPerimeter), - &this->region()->config(), + ®ion_config, &this->layer()->object()->config(), - &this->layer()->object()->print()->config(), + &print_config, + spiral_vase, // output: &this->perimeters, diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index e6261ebd1..ba12a1edb 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -271,7 +271,7 @@ void PerimeterGenerator::process() double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1); m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2))); } - + // we need to process each island separately because we might have different // extra perimeters for each one for (const Surface &surface : this->slices->surfaces) { @@ -312,7 +312,7 @@ void PerimeterGenerator::process() for (ExPolygon &ex : expp) ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls); } - if (print_config->spiral_vase && offsets.size() > 1) { + if (m_spiral_vase && offsets.size() > 1) { // Remove all but the largest area polygon. keep_largest_contour_only(offsets); } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index c0d9e65a7..f55555068 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -37,6 +37,7 @@ public: const PrintRegionConfig* config, const PrintObjectConfig* object_config, const PrintConfig* print_config, + const bool spiral_vase, // Output: // Loops with the external thin walls ExtrusionEntityCollection* loops, @@ -48,6 +49,7 @@ public: layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), solid_infill_flow(flow), config(config), object_config(object_config), print_config(print_config), + m_spiral_vase(spiral_vase), loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1) {} @@ -60,6 +62,7 @@ public: Polygons lower_slices_polygons() const { return m_lower_slices_polygons; } private: + bool m_spiral_vase; double m_ext_mm3_per_mm; double m_mm3_per_mm; double m_mm3_per_mm_overhang; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index f370d751d..1d9f3f722 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -259,9 +259,16 @@ private: // so that next call to make_perimeters() performs a union() before computing loops bool m_typed_slices = false; - std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; + std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const; + std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const + { return this->slice_region(region_id, z, mode, 0, mode); } std::vector slice_modifiers(size_t region_id, const std::vector &z) const; - std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const; + std::vector slice_volumes( + const std::vector &z, + SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below, + const std::vector &volumes) const; + std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const + { return this->slice_volumes(z, mode, 0, mode, volumes); } std::vector slice_volume(const std::vector &z, SlicingMode mode, const ModelVolume &volume) const; std::vector slice_volume(const std::vector &z, const std::vector &ranges, SlicingMode mode, const ModelVolume &volume) const; }; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index a409f12e1..2babb0d73 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1747,14 +1747,24 @@ void PrintObject::_slice(const std::vector &layer_height_profile) // Slice all non-modifier volumes. bool clipped = false; bool upscaled = false; - auto slicing_mode = this->print()->config().spiral_vase ? SlicingMode::PositiveLargestContour : SlicingMode::Regular; + bool spiral_vase = this->print()->config().spiral_vase; + auto slicing_mode = spiral_vase ? SlicingMode::PositiveLargestContour : 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 < this->region_volumes.size(); ++ region_id) { BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; // slicing in parallel - std::vector expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode); + 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->print()->regions()[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, 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) @@ -2001,7 +2011,7 @@ 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, SlicingMode mode) const +std::vector PrintObject::slice_region(size_t region_id, const std::vector &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const { std::vector volumes; if (region_id < this->region_volumes.size()) { @@ -2011,7 +2021,7 @@ std::vector PrintObject::slice_region(size_t region_id, const std::v volumes.emplace_back(volume); } } - return this->slice_volumes(z, mode, volumes); + 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_and_range at most once. @@ -2123,7 +2133,10 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType return this->slice_volumes(zs, SlicingMode::Regular, volumes); } -std::vector PrintObject::slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const +std::vector PrintObject::slice_volumes( + const std::vector &z, + SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below, + const std::vector &volumes) const { std::vector layers; if (! volumes.empty()) { @@ -2153,7 +2166,7 @@ std::vector PrintObject::slice_volumes(const std::vector &z, mesh.require_shared_vertices(); TriangleMeshSlicer mslicer; mslicer.init(&mesh, callback); - mslicer.slice(z, mode, float(m_config.slice_closing_radius.value), &layers, callback); + mslicer.slice(z, mode, slicing_mode_normal_below_layer, mode_below, float(m_config.slice_closing_radius.value), &layers, callback); m_print->throw_if_canceled(); } } diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 8ba34e516..b99a21f11 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -775,9 +775,10 @@ void TriangleMeshSlicer::set_up_direction(const Vec3f& up) m_use_quaternion = true; } - - -void TriangleMeshSlicer::slice(const std::vector &z, SlicingMode mode, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const +void TriangleMeshSlicer::slice( + const std::vector &z, + SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode, + std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; @@ -832,20 +833,21 @@ void TriangleMeshSlicer::slice(const std::vector &z, SlicingMode mode, st layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&lines, &layers, mode, throw_on_cancel, this](const tbb::blocked_range& range) { + [&lines, &layers, mode, alternate_mode_first_n_layers, alternate_mode, throw_on_cancel, this](const tbb::blocked_range& range) { for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) { if ((line_idx & 0x0ffff) == 0) throw_on_cancel(); - Polygons &polygons = (*layers)[line_idx]; + Polygons &polygons = (*layers)[line_idx]; this->make_loops(lines[line_idx], &polygons); + auto this_mode = line_idx < alternate_mode_first_n_layers ? alternate_mode : mode; if (! polygons.empty()) { - if (mode == SlicingMode::Positive) { + if (this_mode == SlicingMode::Positive) { // Reorient all loops to be CCW. for (Polygon& p : polygons) p.make_counter_clockwise(); - } else if (mode == SlicingMode::PositiveLargestContour) { + } else if (this_mode == SlicingMode::PositiveLargestContour) { // Keep just the largest polygon, make it CCW. double max_area = 0.; Polygon* max_area_polygon = nullptr; @@ -941,16 +943,23 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector &z, SlicingMode mode, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const +void TriangleMeshSlicer::slice( + const std::vector &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode, const float closing_radius, + std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { std::vector layers_p; - this->slice(z, (mode == SlicingMode::PositiveLargestContour) ? SlicingMode::Positive : mode, &layers_p, throw_on_cancel); + this->slice(z, + (mode == SlicingMode::PositiveLargestContour) ? SlicingMode::Positive : mode, + alternate_mode_first_n_layers, + (alternate_mode == SlicingMode::PositiveLargestContour) ? SlicingMode::Positive : alternate_mode, + &layers_p, throw_on_cancel); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start"; layers->resize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&layers_p, mode, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range& range) { + [&layers_p, mode, alternate_mode_first_n_layers, alternate_mode, closing_radius, layers, throw_on_cancel, this] + (const tbb::blocked_range& range) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { #ifdef SLIC3R_TRIANGLEMESH_DEBUG printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]); @@ -958,7 +967,8 @@ void TriangleMeshSlicer::slice(const std::vector &z, SlicingMode mode, co throw_on_cancel(); ExPolygons &expolygons = (*layers)[layer_id]; this->make_expolygons(layers_p[layer_id], closing_radius, &expolygons); - if (mode == SlicingMode::PositiveLargestContour) + const auto this_mode = layer_id < alternate_mode_first_n_layers ? alternate_mode : mode; + if (this_mode == SlicingMode::PositiveLargestContour) keep_largest_contour_only(expolygons); } }); diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 723125aaa..71cd231c3 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -179,8 +179,17 @@ public: TriangleMeshSlicer() : mesh(nullptr) {} TriangleMeshSlicer(const TriangleMesh* mesh) { this->init(mesh, [](){}); } void init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); - void slice(const std::vector &z, SlicingMode mode, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; - void slice(const std::vector &z, SlicingMode mode, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice( + const std::vector &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode, + std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, SlicingMode mode, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const + { return this->slice(z, mode, 0, mode, layers, throw_on_cancel); } + void slice( + const std::vector &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode, const float closing_radius, + std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; + void slice(const std::vector &z, SlicingMode mode, const float closing_radius, + std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const + { this->slice(z, mode, 0, mode, closing_radius, layers, throw_on_cancel); } enum FacetSliceType { NoSlice = 0, Slicing = 1, diff --git a/xs/xsp/PerimeterGenerator.xsp b/xs/xsp/PerimeterGenerator.xsp index f2c0d025c..a2f589d0b 100644 --- a/xs/xsp/PerimeterGenerator.xsp +++ b/xs/xsp/PerimeterGenerator.xsp @@ -16,6 +16,7 @@ dynamic_cast(region_config), dynamic_cast(object_config), dynamic_cast(print_config), + false, loops, gap_fill, fill_surfaces); %}; ~PerimeterGenerator();