diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index f9c470450..4a8966044 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -657,4 +657,23 @@ bool remove_sticks(ExPolygon &poly) return remove_sticks(poly.contour) || remove_sticks(poly.holes); } +void keep_largest_contour_only(ExPolygons &polygons) +{ + if (polygons.size() > 1) { + double max_area = 0.; + ExPolygon* max_area_polygon = nullptr; + for (ExPolygon& p : polygons) { + double a = p.contour.area(); + if (a > max_area) { + max_area = a; + max_area_polygon = &p; + } + } + assert(max_area_polygon != nullptr); + ExPolygon p(std::move(*max_area_polygon)); + polygons.clear(); + polygons.emplace_back(std::move(p)); + } +} + } // namespace Slic3r diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index 7c0dfcce5..4aad3603f 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -327,6 +327,7 @@ extern BoundingBox get_extents_rotated(const ExPolygons &polygons, double angle) extern std::vector get_extents_vector(const ExPolygons &polygons); extern bool remove_sticks(ExPolygon &poly); +extern void keep_largest_contour_only(ExPolygons &polygons); extern std::list expoly_to_polypartition_input(const ExPolygons &expp); extern std::list expoly_to_polypartition_input(const ExPolygon &ex); diff --git a/src/libslic3r/GCode/SpiralVase.cpp b/src/libslic3r/GCode/SpiralVase.cpp index a4ae42b31..a3c190069 100644 --- a/src/libslic3r/GCode/SpiralVase.cpp +++ b/src/libslic3r/GCode/SpiralVase.cpp @@ -16,8 +16,8 @@ std::string SpiralVase::process_layer(const std::string &gcode) // If we're not going to modify G-code, just feed it to the reader // in order to update positions. - if (!this->enable) { - this->_reader.parse_buffer(gcode); + if (! this->enable) { + m_reader.parse_buffer(gcode); return gcode; } @@ -29,7 +29,7 @@ std::string SpiralVase::process_layer(const std::string &gcode) { //FIXME Performance warning: This copies the GCodeConfig of the reader. - GCodeReader r = this->_reader; // clone + GCodeReader r = m_reader; // clone r.parse_buffer(gcode, [&total_layer_length, &layer_height, &z, &set_z] (GCodeReader &reader, const GCodeReader::GCodeLine &line) { if (line.cmd_is("G1")) { @@ -50,7 +50,7 @@ std::string SpiralVase::process_layer(const std::string &gcode) z -= layer_height; std::string new_gcode; - this->_reader.parse_buffer(gcode, [&new_gcode, &z, &layer_height, &total_layer_length] + m_reader.parse_buffer(gcode, [&new_gcode, &z, &layer_height, &total_layer_length] (GCodeReader &reader, GCodeReader::GCodeLine line) { if (line.cmd_is("G1")) { if (line.has_z()) { diff --git a/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp index e35ca640c..496c1425c 100644 --- a/src/libslic3r/GCode/SpiralVase.hpp +++ b/src/libslic3r/GCode/SpiralVase.hpp @@ -7,20 +7,19 @@ namespace Slic3r { class SpiralVase { - public: - bool enable; +public: + bool enable = false; - SpiralVase(const PrintConfig &config) - : enable(false), _config(&config) + SpiralVase(const PrintConfig &config) : m_config(&config) { - this->_reader.z() = (float)this->_config->z_offset; - this->_reader.apply_config(*this->_config); + m_reader.z() = (float)m_config->z_offset; + m_reader.apply_config(*m_config); }; std::string process_layer(const std::string &gcode); - private: - const PrintConfig* _config; - GCodeReader _reader; +private: + const PrintConfig *m_config; + GCodeReader m_reader; }; } diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index f4f0d6a5d..67a1acb09 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -362,8 +362,10 @@ void LayerRegion::prepare_fill_surfaces() alter fill_surfaces boundaries on which our idempotency relies since that's the only meaningful information returned by psPerimeters. */ + bool spiral_vase = this->layer()->object()->print()->config().spiral_vase; + // if no solid layers are requested, turn top/bottom surfaces to internal - if (this->region()->config().top_solid_layers == 0) { + if (! spiral_vase && this->region()->config().top_solid_layers == 0) { for (Surface &surface : this->fill_surfaces.surfaces) if (surface.is_top()) surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal; @@ -375,7 +377,7 @@ void LayerRegion::prepare_fill_surfaces() } // turn too small internal regions into solid regions according to the user setting - if (this->region()->config().fill_density.value > 0) { + if (! spiral_vase && this->region()->config().fill_density.value > 0) { // scaling an area requires two calls! double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value)); for (Surface &surface : this->fill_surfaces.surfaces) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 450fff351..3cd91dafe 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -312,6 +312,10 @@ 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) { + // Remove all but the largest area polygon. + keep_largest_contour_only(offsets); + } } else { //FIXME Is this offset correct if the line width of the inner perimeters differs // from the line width of the infill? diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 359d162f3..7b326472e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -24,6 +24,7 @@ class PrintObject; class ModelObject; class GCode; class GCodePreviewData; +enum class SlicingMode : uint32_t; // Print step IDs for keeping track of the print state. enum PrintStep { @@ -253,11 +254,11 @@ 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) const; + std::vector slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const; std::vector slice_modifiers(size_t region_id, const std::vector &z) const; - std::vector slice_volumes(const std::vector &z, const std::vector &volumes) const; - std::vector slice_volume(const std::vector &z, const ModelVolume &volume) const; - std::vector slice_volume(const std::vector &z, const std::vector &ranges, const ModelVolume &volume) const; + std::vector slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const; + 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; }; struct WipeTowerData diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 5d0faa19d..04277f931 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -463,10 +463,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector 0; } +static const PrintRegion* first_printing_region(const PrintObject &print_object) +{ + for (size_t idx_region = 0; idx_region < print_object.region_volumes.size(); ++ idx_region) + if (!print_object.region_volumes.empty()) + return print_object.print()->regions()[idx_region]; + return nullptr; +} + // This function analyzes slices of a region (SurfaceCollection slices). // Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. // Initially all slices are of type stInternal. @@ -642,7 +650,9 @@ void PrintObject::detect_surfaces_type() // are completely hidden inside a collective body of intersecting parts. // This is useful if one of the parts is to be dissolved, or if it is transparent and the internal shells // should be visible. - bool interface_shells = m_config.interface_shells.value; + bool spiral_vase = this->print()->config().spiral_vase.value; + bool interface_shells = ! spiral_vase && m_config.interface_shells.value; + size_t num_layers = spiral_vase ? first_printing_region(*this)->config().bottom_solid_layers : m_layers.size(); for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; @@ -655,10 +665,15 @@ void PrintObject::detect_surfaces_type() // Cache the result of the following parallel_loop. std::vector surfaces_new; if (interface_shells) - surfaces_new.assign(m_layers.size(), Surfaces()); + surfaces_new.assign(num_layers, Surfaces()); tbb::parallel_for( - tbb::blocked_range(0, m_layers.size()), + tbb::blocked_range(0, + spiral_vase ? + // In spiral vase mode, reserve the last layer for the top surface if more than 1 layer is planned for the vase bottom. + ((num_layers > 1) ? num_layers - 1 : num_layers) : + // In non-spiral vase mode, go over all layers. + m_layers.size()), [this, idx_region, interface_shells, &surfaces_new](const tbb::blocked_range& range) { // If we have raft layers, consider bottom layer as a bridge just like any other bottom surface lying on the void. SurfaceType surface_type_bottom_1st = @@ -799,10 +814,17 @@ void PrintObject::detect_surfaces_type() if (interface_shells) { // Move surfaces_new to layerm->slices.surfaces - for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer) + for (size_t idx_layer = 0; idx_layer < num_layers; ++ idx_layer) m_layers[idx_layer]->m_regions[idx_region]->slices.surfaces = std::move(surfaces_new[idx_layer]); } + if (spiral_vase && num_layers > 1) { + // Turn the last bottom layer infill to a top infill, so it will be extruded with a proper pattern. + Surfaces &surfaces = m_layers[num_layers - 1]->m_regions[idx_region]->slices.surfaces; + for (Surface &surface : surfaces) + surface.surface_type = stTop; + } + BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " - clipping in parallel - start"; // Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. tbb::parallel_for( @@ -916,6 +938,8 @@ void PrintObject::discover_vertical_shells() Polygons bottom_surfaces; Polygons holes; }; + bool spiral_vase = this->print()->config().spiral_vase.value; + size_t num_layers = spiral_vase ? first_printing_region(*this)->config().bottom_solid_layers : m_layers.size(); coordf_t min_layer_height = this->slicing_parameters().min_layer_height; // Does this region possibly produce more than 1 top or bottom layer? auto has_extra_layers_fn = [min_layer_height](const PrintRegionConfig &config) { @@ -929,7 +953,7 @@ void PrintObject::discover_vertical_shells() return num_extra_layers(config.top_solid_layers, config.top_solid_min_thickness) + num_extra_layers(config.bottom_solid_layers, config.bottom_solid_min_thickness) > 0; }; - std::vector cache_top_botom_regions(m_layers.size(), DiscoverVerticalShellsCacheEntry()); + std::vector cache_top_botom_regions(num_layers, DiscoverVerticalShellsCacheEntry()); bool top_bottom_surfaces_all_regions = this->region_volumes.size() > 1 && ! m_config.interface_shells.value; if (top_bottom_surfaces_all_regions) { // This is a multi-material print and interface_shells are disabled, meaning that the vertical shell thickness @@ -948,9 +972,9 @@ void PrintObject::discover_vertical_shells() return; BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells in parallel - start : cache top / bottom"; //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); + size_t grain_size = std::max(num_layers / 16, size_t(1)); tbb::parallel_for( - tbb::blocked_range(0, m_layers.size(), grain_size), + tbb::blocked_range(0, num_layers, grain_size), [this, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; const size_t num_regions = this->region_volumes.size(); @@ -999,8 +1023,8 @@ void PrintObject::discover_vertical_shells() polygons_append(cache.holes, offset(offset_ex(layer.lslices, 0.3f * perimeter_min_spacing), - perimeter_offset - 0.3f * perimeter_min_spacing)); #ifdef SLIC3R_DEBUG_SLICE_PROCESSING { - Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.slices)); - svg.draw(layer.slices, "blue"); + Slic3r::SVG svg(debug_out_path("discover_vertical_shells-extra-holes-%d.svg", debug_idx), get_extents(layer.lslices)); + svg.draw(layer.lslices, "blue"); svg.draw(union_ex(cache.holes), "red"); svg.draw_outline(union_ex(cache.holes), "black", "blue", scale_(0.05)); svg.Close(); @@ -1026,14 +1050,14 @@ void PrintObject::discover_vertical_shells() continue; //FIXME Improve the heuristics for a grain size. - size_t grain_size = std::max(m_layers.size() / 16, size_t(1)); + size_t grain_size = std::max(num_layers / 16, size_t(1)); if (! top_bottom_surfaces_all_regions) { // This is either a single material print, or a multi-material print and interface_shells are enabled, meaning that the vertical shell thickness // is calculated over a single material. BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : cache top / bottom"; tbb::parallel_for( - tbb::blocked_range(0, m_layers.size(), grain_size), + tbb::blocked_range(0, num_layers, grain_size), [this, idx_region, &cache_top_botom_regions](const tbb::blocked_range& range) { const SurfaceType surfaces_bottom[2] = { stBottom, stBottomBridge }; for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) { @@ -1061,7 +1085,7 @@ void PrintObject::discover_vertical_shells() BOOST_LOG_TRIVIAL(debug) << "Discovering vertical shells for region " << idx_region << " in parallel - start : ensure vertical wall thickness"; tbb::parallel_for( - tbb::blocked_range(0, m_layers.size(), grain_size), + tbb::blocked_range(0, num_layers, grain_size), [this, idx_region, &cache_top_botom_regions] (const tbb::blocked_range& range) { // printf("discover_vertical_shells from %d to %d\n", range.begin(), range.end()); @@ -1635,13 +1659,14 @@ 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; 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); + std::vector expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode); 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) @@ -1679,7 +1704,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) else ranges.emplace_back(volumes_and_ranges[j].first); // slicing in parallel - sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, *model_volume)); + sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume)); i = j; } else ++ i; @@ -1888,7 +1913,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) const +std::vector PrintObject::slice_region(size_t region_id, const std::vector &z, SlicingMode mode) const { std::vector volumes; if (region_id < this->region_volumes.size()) { @@ -1898,7 +1923,7 @@ std::vector PrintObject::slice_region(size_t region_id, const std::v volumes.emplace_back(volume); } } - return this->slice_volumes(z, volumes); + return this->slice_volumes(z, mode, volumes); } // Z ranges are not applicable to modifier meshes, therefore a sinle volume will be found in volume_and_range at most once. @@ -1948,7 +1973,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, volumes); + out = this->slice_volumes(slice_zs, SlicingMode::Regular, volumes); } else { // Some modifier in this region was split to layer spans. std::vector merge; @@ -1966,7 +1991,7 @@ std::vector PrintObject::slice_modifiers(size_t region_id, const std for (; j < volumes_and_ranges.size() && volume_id == volumes_and_ranges[j].second; ++ j) ranges.emplace_back(volumes_and_ranges[j].first); // slicing in parallel - std::vector this_slices = this->slice_volume(slice_zs, ranges, *model_volume); + std::vector this_slices = this->slice_volume(slice_zs, ranges, SlicingMode::Regular, *model_volume); if (out.empty()) { out = std::move(this_slices); merge.assign(out.size(), false); @@ -2005,10 +2030,10 @@ std::vector PrintObject::slice_support_volumes(const ModelVolumeType zs.reserve(this->layers().size()); for (const Layer *l : this->layers()) zs.emplace_back((float)l->slice_z); - return this->slice_volumes(zs, volumes); + return this->slice_volumes(zs, SlicingMode::Regular, volumes); } -std::vector PrintObject::slice_volumes(const std::vector &z, const std::vector &volumes) const +std::vector PrintObject::slice_volumes(const std::vector &z, SlicingMode mode, const std::vector &volumes) const { std::vector layers; if (! volumes.empty()) { @@ -2038,14 +2063,14 @@ std::vector PrintObject::slice_volumes(const std::vector &z, mesh.require_shared_vertices(); TriangleMeshSlicer mslicer; mslicer.init(&mesh, callback); - mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); + mslicer.slice(z, mode, float(m_config.slice_closing_radius.value), &layers, callback); m_print->throw_if_canceled(); } } return layers; } -std::vector PrintObject::slice_volume(const std::vector &z, const ModelVolume &volume) const +std::vector PrintObject::slice_volume(const std::vector &z, SlicingMode mode, const ModelVolume &volume) const { std::vector layers; if (! z.empty()) { @@ -2068,7 +2093,7 @@ std::vector PrintObject::slice_volume(const std::vector &z, c // TriangleMeshSlicer needs the shared vertices. mesh.require_shared_vertices(); mslicer.init(&mesh, callback); - mslicer.slice(z, float(m_config.slice_closing_radius.value), &layers, callback); + mslicer.slice(z, mode, float(m_config.slice_closing_radius.value), &layers, callback); m_print->throw_if_canceled(); } } @@ -2076,13 +2101,13 @@ std::vector PrintObject::slice_volume(const std::vector &z, c } // Filter the zs not inside the ranges. The ranges are closed at the botton 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, const ModelVolume &volume) const +std::vector PrintObject::slice_volume(const std::vector &z, const std::vector &ranges, 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, volume); + out = this->slice_volume(z, mode, volume); } else { std::vector z_filtered; std::vector> n_filtered; @@ -2098,7 +2123,7 @@ std::vector PrintObject::slice_volume(const std::vector &z, c n_filtered.emplace_back(std::make_pair(first, i)); } if (! n_filtered.empty()) { - std::vector layers = this->slice_volume(z_filtered, volume); + std::vector layers = this->slice_volume(z_filtered, mode, volume); out.assign(z.size(), ExPolygons()); i = 0; for (const std::pair &span : n_filtered) @@ -2453,7 +2478,8 @@ void PrintObject::discover_horizontal_shells() // is grown, and that little space is an internal solid shell so // it triggers this too_narrow logic.) internal)); - solid = new_internal_solid; + // see https://github.com/prusa3d/PrusaSlicer/pull/3426 + // solid = new_internal_solid; } } diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index 2a9662e85..c4a616d93 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -262,7 +262,7 @@ void cut_drainholes(std::vector & obj_slices, TriangleMeshSlicer slicer(&mesh); std::vector hole_slices; - slicer.slice(slicegrid, closing_radius, &hole_slices, thr); + slicer.slice(slicegrid, SlicingMode::Regular, closing_radius, &hole_slices, thr); if (obj_slices.size() != hole_slices.size()) BOOST_LOG_TRIVIAL(warning) diff --git a/src/libslic3r/SLA/Pad.cpp b/src/libslic3r/SLA/Pad.cpp index 742f0db1b..cf1786758 100644 --- a/src/libslic3r/SLA/Pad.cpp +++ b/src/libslic3r/SLA/Pad.cpp @@ -638,7 +638,7 @@ void pad_blueprint(const TriangleMesh & mesh, TriangleMeshSlicer slicer(&mesh); auto out = reserve_vector(heights.size()); - slicer.slice(heights, 0.f, &out, thrfn); + slicer.slice(heights, SlicingMode::Regular, 0.f, &out, thrfn); size_t count = 0; for(auto& o : out) count += o.size(); diff --git a/src/libslic3r/SLA/SupportTree.cpp b/src/libslic3r/SLA/SupportTree.cpp index 3024883ed..5ee35f9e0 100644 --- a/src/libslic3r/SLA/SupportTree.cpp +++ b/src/libslic3r/SLA/SupportTree.cpp @@ -61,7 +61,7 @@ std::vector SupportTree::slice( slices.emplace_back(); TriangleMeshSlicer sup_slicer(&sup_mesh); - sup_slicer.slice(grid, cr, &slices.back(), ctl().cancelfn); + sup_slicer.slice(grid, SlicingMode::Regular, cr, &slices.back(), ctl().cancelfn); } if (!pad_mesh.empty()) { @@ -75,7 +75,7 @@ std::vector SupportTree::slice( std::copy(grid.begin(), maxzit, std::back_inserter(padgrid)); TriangleMeshSlicer pad_slicer(&pad_mesh); - pad_slicer.slice(padgrid, cr, &slices.back(), ctl().cancelfn); + pad_slicer.slice(padgrid, SlicingMode::Regular, cr, &slices.back(), ctl().cancelfn); } size_t len = grid.size(); diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 3fb99afbf..ff170d917 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -220,7 +220,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) float closing_r = float(po.config().slice_closing_radius.value); auto thr = [this]() { m_print->throw_if_canceled(); }; auto &slice_grid = po.m_model_height_levels; - slicer.slice(slice_grid, closing_r, &po.m_model_slices, thr); + slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr); // sla::DrainHoles drainholes = po.transformed_drainhole_points(); // cut_drainholes(po.m_model_slices, slice_grid, closing_r, drainholes, thr); diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 4c6cd62cf..ee5e96f3d 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -627,7 +627,7 @@ std::vector TriangleMesh::slice(const std::vector &z) std::vector z_f(z.begin(), z.end()); TriangleMeshSlicer mslicer(this); std::vector layers; - mslicer.slice(z_f, 0.0004f, &layers, [](){}); + mslicer.slice(z_f, SlicingMode::Regular, 0.0004f, &layers, [](){}); return layers; } @@ -776,7 +776,7 @@ void TriangleMeshSlicer::set_up_direction(const Vec3f& up) -void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const +void TriangleMeshSlicer::slice(const std::vector &z, SlicingMode mode, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; @@ -831,11 +831,38 @@ void TriangleMeshSlicer::slice(const std::vector &z, std::vectorresize(z.size()); tbb::parallel_for( tbb::blocked_range(0, z.size()), - [&lines, &layers, throw_on_cancel, this](const tbb::blocked_range& range) { + [&lines, &layers, 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(); - this->make_loops(lines[line_idx], &(*layers)[line_idx]); + + Polygons &polygons = (*layers)[line_idx]; + this->make_loops(lines[line_idx], &polygons); + + if (! polygons.empty()) { + if (mode == SlicingMode::Positive) { + // Reorient all loops to be CCW. + for (Polygon& p : polygons) + p.make_counter_clockwise(); + } else if (mode == SlicingMode::PositiveLargestContour) { + // Keep just the largest polygon, make it CCW. + double max_area = 0.; + Polygon* max_area_polygon = nullptr; + for (Polygon& p : polygons) { + double a = p.area(); + if (std::abs(a) > std::abs(max_area)) { + max_area = a; + max_area_polygon = &p; + } + } + assert(max_area_polygon != nullptr); + if (max_area < 0.) + max_area_polygon->reverse(); + Polygon p(std::move(*max_area_polygon)); + polygons.clear(); + polygons.emplace_back(std::move(p)); + } + } } } ); @@ -913,22 +940,25 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector &z, 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, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const { std::vector layers_p; - this->slice(z, &layers_p, throw_on_cancel); + this->slice(z, (mode == SlicingMode::PositiveLargestContour) ? SlicingMode::Positive : 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, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range& range) { + [&layers_p, 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 " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]); #endif throw_on_cancel(); - this->make_expolygons(layers_p[layer_id], closing_radius, &(*layers)[layer_id]); + ExPolygons &expolygons = (*layers)[layer_id]; + this->make_expolygons(layers_p[layer_id], closing_radius, &expolygons); + if (mode == SlicingMode::PositiveLargestContour) + keep_largest_contour_only(expolygons); } }); BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end"; diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 1a22a9343..bd872a975 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -162,6 +162,16 @@ public: typedef std::vector IntersectionLines; typedef std::vector IntersectionLinePtrs; +enum class SlicingMode : uint32_t { + // Regular slicing, maintain all contours and their orientation. + Regular, + // Maintain all contours, orient all contours CCW, therefore all holes are being closed. + Positive, + // Orient all contours CCW and keep only the contour with the largest area. + // This mode is useful for slicing complex objects in vase mode. + PositiveLargestContour, +}; + class TriangleMeshSlicer { public: @@ -169,8 +179,8 @@ 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, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; - void slice(const std::vector &z, const float closing_radius, 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; + void slice(const std::vector &z, SlicingMode mode, const float closing_radius, std::vector* layers, throw_on_cancel_callback_type throw_on_cancel) const; enum FacetSliceType { NoSlice = 0, Slicing = 1, @@ -207,7 +217,7 @@ inline void slice_mesh( { if (mesh.empty()) return; TriangleMeshSlicer slicer(&mesh); - slicer.slice(z, &layers, thr); + slicer.slice(z, SlicingMode::Regular, &layers, thr); } inline void slice_mesh( @@ -219,7 +229,7 @@ inline void slice_mesh( { if (mesh.empty()) return; TriangleMeshSlicer slicer(&mesh); - slicer.slice(z, closing_radius, &layers, thr); + slicer.slice(z, SlicingMode::Regular, closing_radius, &layers, thr); } TriangleMesh make_cube(double x, double y, double z); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index a8773d736..0d38ee566 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -70,14 +70,21 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con double fill_density = config->option("fill_density")->value; if (config->opt_bool("spiral_vase") && - !(config->opt_int("perimeters") == 1 && config->opt_int("top_solid_layers") == 0 && - fill_density == 0)) { + ! (config->opt_int("perimeters") == 1 && + config->opt_int("top_solid_layers") == 0 && + fill_density == 0 && + ! config->opt_bool("support_material") && + config->opt_int("support_material_enforce_layers") == 0 && + config->opt_bool("ensure_vertical_shell_thickness") && + ! config->opt_bool("thin_walls"))) + { wxString msg_text = _(L("The Spiral Vase mode requires:\n" "- one perimeter\n" "- no top solid layers\n" "- 0% fill density\n" "- no support material\n" - "- inactive Ensure vertical shell thickness")); + "- Ensure vertical shell thickness enabled\n" + "- Detect thin walls disabled")); if (is_global_config) msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable Spiral Vase?")); wxMessageDialog dialog(nullptr, msg_text, _(L("Spiral Vase")), @@ -90,7 +97,8 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con new_conf.set_key_value("fill_density", new ConfigOptionPercent(0)); new_conf.set_key_value("support_material", new ConfigOptionBool(false)); new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(false)); + new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true)); + new_conf.set_key_value("thin_walls", new ConfigOptionBool(false)); fill_density = 0; } else { diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index 3bf99f73c..37b6efd87 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -74,7 +74,7 @@ void MeshClipper::recalculate_triangles() // Now do the cutting std::vector list_of_expolys; m_tms->set_up_direction(up); - m_tms->slice(std::vector{height_mesh}, 0.f, &list_of_expolys, [](){}); + m_tms->slice(std::vector{height_mesh}, SlicingMode::Regular, 0.f, &list_of_expolys, [](){}); m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); // Rotate the cut into world coords: