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
This commit is contained in:
Vojtech Bubnik 2020-12-09 14:07:22 +01:00
parent 5470af3938
commit 364300055e
8 changed files with 77 additions and 26 deletions

View File

@ -54,15 +54,23 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
{ {
this->perimeters.clear(); this->perimeters.clear();
this->thin_fills.clear(); this->thin_fills.clear();
const PrintConfig &print_config = this->layer()->object()->print()->config();
const PrintRegionConfig &region_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( PerimeterGenerator g(
// input: // input:
&slices, &slices,
this->layer()->height, this->layer()->height,
this->flow(frPerimeter), this->flow(frPerimeter),
&this->region()->config(), &region_config,
&this->layer()->object()->config(), &this->layer()->object()->config(),
&this->layer()->object()->print()->config(), &print_config,
spiral_vase,
// output: // output:
&this->perimeters, &this->perimeters,

View File

@ -271,7 +271,7 @@ void PerimeterGenerator::process()
double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1); 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))); 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 // we need to process each island separately because we might have different
// extra perimeters for each one // extra perimeters for each one
for (const Surface &surface : this->slices->surfaces) { for (const Surface &surface : this->slices->surfaces) {
@ -312,7 +312,7 @@ void PerimeterGenerator::process()
for (ExPolygon &ex : expp) for (ExPolygon &ex : expp)
ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls); 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. // Remove all but the largest area polygon.
keep_largest_contour_only(offsets); keep_largest_contour_only(offsets);
} }

View File

@ -37,6 +37,7 @@ public:
const PrintRegionConfig* config, const PrintRegionConfig* config,
const PrintObjectConfig* object_config, const PrintObjectConfig* object_config,
const PrintConfig* print_config, const PrintConfig* print_config,
const bool spiral_vase,
// Output: // Output:
// Loops with the external thin walls // Loops with the external thin walls
ExtrusionEntityCollection* loops, ExtrusionEntityCollection* loops,
@ -48,6 +49,7 @@ public:
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
overhang_flow(flow), solid_infill_flow(flow), overhang_flow(flow), solid_infill_flow(flow),
config(config), object_config(object_config), print_config(print_config), 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), 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) 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; } Polygons lower_slices_polygons() const { return m_lower_slices_polygons; }
private: private:
bool m_spiral_vase;
double m_ext_mm3_per_mm; double m_ext_mm3_per_mm;
double m_mm3_per_mm; double m_mm3_per_mm;
double m_mm3_per_mm_overhang; double m_mm3_per_mm_overhang;

View File

@ -259,9 +259,16 @@ private:
// so that next call to make_perimeters() performs a union() before computing loops // so that next call to make_perimeters() performs a union() before computing loops
bool m_typed_slices = false; bool m_typed_slices = false;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode) const; std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const;
std::vector<ExPolygons> slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode) const
{ return this->slice_region(region_id, z, mode, 0, mode); }
std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const; std::vector<ExPolygons> slice_modifiers(size_t region_id, const std::vector<float> &z) const;
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const; std::vector<ExPolygons> slice_volumes(
const std::vector<float> &z,
SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below,
const std::vector<const ModelVolume*> &volumes) const;
std::vector<ExPolygons> slice_volumes(const std::vector<float> &z, SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const
{ return this->slice_volumes(z, mode, 0, mode, volumes); }
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, SlicingMode mode, const ModelVolume &volume) const; std::vector<ExPolygons> slice_volume(const std::vector<float> &z, SlicingMode mode, const ModelVolume &volume) const;
std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const; std::vector<ExPolygons> slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, SlicingMode mode, const ModelVolume &volume) const;
}; };

View File

@ -1747,14 +1747,24 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
// Slice all non-modifier volumes. // Slice all non-modifier volumes.
bool clipped = false; bool clipped = false;
bool upscaled = 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)) { if (! has_z_ranges && (! m_config.clip_multipart_objects.value || all_volumes_single_region >= 0)) {
// Cheap path: Slice regions without mutual clipping. // 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. // 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) { for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id; BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel // slicing in parallel
std::vector<ExPolygons> 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> expolygons_by_layer = this->slice_region(region_id, slice_zs, slicing_mode, slicing_mode_normal_below_layer, SlicingMode::Regular);
m_print->throw_if_canceled(); m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " start"; 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) 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. // 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<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode) const std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below) const
{ {
std::vector<const ModelVolume*> volumes; std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) { if (region_id < this->region_volumes.size()) {
@ -2011,7 +2021,7 @@ std::vector<ExPolygons> PrintObject::slice_region(size_t region_id, const std::v
volumes.emplace_back(volume); 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. // 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<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType
return this->slice_volumes(zs, SlicingMode::Regular, volumes); return this->slice_volumes(zs, SlicingMode::Regular, volumes);
} }
std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z, SlicingMode mode, const std::vector<const ModelVolume*> &volumes) const std::vector<ExPolygons> PrintObject::slice_volumes(
const std::vector<float> &z,
SlicingMode mode, size_t slicing_mode_normal_below_layer, SlicingMode mode_below,
const std::vector<const ModelVolume*> &volumes) const
{ {
std::vector<ExPolygons> layers; std::vector<ExPolygons> layers;
if (! volumes.empty()) { if (! volumes.empty()) {
@ -2153,7 +2166,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(const std::vector<float> &z,
mesh.require_shared_vertices(); mesh.require_shared_vertices();
TriangleMeshSlicer mslicer; TriangleMeshSlicer mslicer;
mslicer.init(&mesh, callback); 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(); m_print->throw_if_canceled();
} }
} }

View File

@ -775,9 +775,10 @@ void TriangleMeshSlicer::set_up_direction(const Vec3f& up)
m_use_quaternion = true; m_use_quaternion = true;
} }
void TriangleMeshSlicer::slice(
const std::vector<float> &z,
void TriangleMeshSlicer::slice(const std::vector<float> &z, SlicingMode mode, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode,
std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{ {
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
@ -832,20 +833,21 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, SlicingMode mode, st
layers->resize(z.size()); layers->resize(z.size());
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()), tbb::blocked_range<size_t>(0, z.size()),
[&lines, &layers, mode, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) { [&lines, &layers, mode, alternate_mode_first_n_layers, alternate_mode, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) { for (size_t line_idx = range.begin(); line_idx < range.end(); ++ line_idx) {
if ((line_idx & 0x0ffff) == 0) if ((line_idx & 0x0ffff) == 0)
throw_on_cancel(); throw_on_cancel();
Polygons &polygons = (*layers)[line_idx]; Polygons &polygons = (*layers)[line_idx];
this->make_loops(lines[line_idx], &polygons); this->make_loops(lines[line_idx], &polygons);
auto this_mode = line_idx < alternate_mode_first_n_layers ? alternate_mode : mode;
if (! polygons.empty()) { if (! polygons.empty()) {
if (mode == SlicingMode::Positive) { if (this_mode == SlicingMode::Positive) {
// Reorient all loops to be CCW. // Reorient all loops to be CCW.
for (Polygon& p : polygons) for (Polygon& p : polygons)
p.make_counter_clockwise(); p.make_counter_clockwise();
} else if (mode == SlicingMode::PositiveLargestContour) { } else if (this_mode == SlicingMode::PositiveLargestContour) {
// Keep just the largest polygon, make it CCW. // Keep just the largest polygon, make it CCW.
double max_area = 0.; double max_area = 0.;
Polygon* max_area_polygon = nullptr; Polygon* max_area_polygon = nullptr;
@ -941,16 +943,23 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
} }
} }
void TriangleMeshSlicer::slice(const std::vector<float> &z, SlicingMode mode, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const void TriangleMeshSlicer::slice(
const std::vector<float> &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode, const float closing_radius,
std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{ {
std::vector<Polygons> layers_p; std::vector<Polygons> 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"; BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - start";
layers->resize(z.size()); layers->resize(z.size());
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, z.size()), tbb::blocked_range<size_t>(0, z.size()),
[&layers_p, mode, closing_radius, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) { [&layers_p, mode, alternate_mode_first_n_layers, alternate_mode, closing_radius, layers, throw_on_cancel, this]
(const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) { for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]); printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]);
@ -958,7 +967,8 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, SlicingMode mode, co
throw_on_cancel(); throw_on_cancel();
ExPolygons &expolygons = (*layers)[layer_id]; ExPolygons &expolygons = (*layers)[layer_id];
this->make_expolygons(layers_p[layer_id], closing_radius, &expolygons); 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); keep_largest_contour_only(expolygons);
} }
}); });

View File

@ -179,8 +179,17 @@ public:
TriangleMeshSlicer() : mesh(nullptr) {} TriangleMeshSlicer() : mesh(nullptr) {}
TriangleMeshSlicer(const TriangleMesh* mesh) { this->init(mesh, [](){}); } TriangleMeshSlicer(const TriangleMesh* mesh) { this->init(mesh, [](){}); }
void init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel); void init(const TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(const std::vector<float> &z, SlicingMode mode, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const; void slice(
void slice(const std::vector<float> &z, SlicingMode mode, const float closing_radius, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const; const std::vector<float> &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode,
std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, SlicingMode mode, std::vector<Polygons>* 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<float> &z, SlicingMode mode, size_t alternate_mode_first_n_layers, SlicingMode alternate_mode, const float closing_radius,
std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, SlicingMode mode, const float closing_radius,
std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const
{ this->slice(z, mode, 0, mode, closing_radius, layers, throw_on_cancel); }
enum FacetSliceType { enum FacetSliceType {
NoSlice = 0, NoSlice = 0,
Slicing = 1, Slicing = 1,

View File

@ -16,6 +16,7 @@
dynamic_cast<PrintRegionConfig*>(region_config), dynamic_cast<PrintRegionConfig*>(region_config),
dynamic_cast<PrintObjectConfig*>(object_config), dynamic_cast<PrintObjectConfig*>(object_config),
dynamic_cast<PrintConfig*>(print_config), dynamic_cast<PrintConfig*>(print_config),
false,
loops, gap_fill, fill_surfaces); %}; loops, gap_fill, fill_surfaces); %};
~PerimeterGenerator(); ~PerimeterGenerator();