From 4e11552da980f07216f1720f67f539703ed90ed1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 8 Feb 2020 21:36:29 +0100 Subject: [PATCH] Spiral vase improvements and bugfixes. Fixes Connecting / expanding Bottom Layers to Vase Perimeter #253 Fixes Slicing error in vase mode #452 Fixes Slicing Issue (Vase Mode, 0.6mm dmr nozzle) #1887 Fixes Top fill pattern isn't used in spiral vase mode #2533 Fixes Cisar's vase doesn't slice correctly, creates artefacts #3595 When the model is sliced, all the contours are newly oriented counter-clockwise (even holes), merged and then only the largest area contour is retained. In perimeter generator, if the largest contour splits into multiple perimeters, newly only the largest area perimeter is retained in spiral vase mode. These two changes solve #3595 and similar. The infill is newly calculated only for the bottom solid layers if the spiral vase mode is active (removes various unwanted infill along the vase walls), and the last bottom solid layer is switched to a top solid pattern (solves #2533). The thin walls are newly enforced to be disabled in spiral vase mode, and the "ensure vertical shell wall" is enforced in spiral vase mode to extend the bottom of the vase to the vase hull (fixes #253). --- src/libslic3r/ExPolygon.cpp | 19 ++++++ src/libslic3r/ExPolygon.hpp | 1 + src/libslic3r/GCode/SpiralVase.cpp | 8 +-- src/libslic3r/GCode/SpiralVase.hpp | 17 +++--- src/libslic3r/LayerRegion.cpp | 6 +- src/libslic3r/PerimeterGenerator.cpp | 4 ++ src/libslic3r/Print.hpp | 9 +-- src/libslic3r/PrintObject.cpp | 86 +++++++++++++++++---------- src/libslic3r/SLA/Hollowing.cpp | 2 +- src/libslic3r/SLA/Pad.cpp | 2 +- src/libslic3r/SLA/SupportTree.cpp | 4 +- src/libslic3r/SLAPrintSteps.cpp | 2 +- src/libslic3r/TriangleMesh.cpp | 46 +++++++++++--- src/libslic3r/TriangleMesh.hpp | 18 ++++-- src/slic3r/GUI/ConfigManipulation.cpp | 16 +++-- src/slic3r/GUI/MeshUtils.cpp | 2 +- 16 files changed, 171 insertions(+), 71 deletions(-) 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: