WIP: Refactoring of PrintRegions. It nearly compiles!

This commit is contained in:
Vojtech Bubnik 2021-05-21 17:57:37 +02:00
parent 68b0d92183
commit 740773db85
8 changed files with 479 additions and 1206 deletions

View file

@ -101,58 +101,12 @@ PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
std::vector<std::reference_wrapper<const PrintRegion>> PrintObject::all_regions() const
{
std::vector<std::reference_wrapper<const PrintRegion>> out;
out.reserve(m_all_regions.size());
for (size_t i = 0; i < m_all_regions.size(); ++ i)
out.emplace_back(*m_all_regions[i]);
out.reserve(m_shared_regions->all_regions.size());
for (const std::unique_ptr<Slic3r::PrintRegion> &region : m_shared_regions->all_regions)
out.emplace_back(*region.get());
return out;
}
// Called by make_perimeters()
// 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions
// 3) Slices the object meshes
// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
// 5) Applies size compensation (offsets the slices in XY plane)
// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
// Resulting expolygons of layer regions are marked as Internal.
void PrintObject::slice()
{
if (! this->set_started(posSlice))
return;
m_print->set_status(10, L("Processing triangulated mesh"));
std::vector<coordf_t> layer_height_profile;
this->update_layer_height_profile(*this->model_object(), m_slicing_params, layer_height_profile);
m_print->throw_if_canceled();
this->_slice(layer_height_profile);
m_print->throw_if_canceled();
// Fix the model.
//FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
std::string warning = this->_fix_slicing_errors();
m_print->throw_if_canceled();
if (! warning.empty())
BOOST_LOG_TRIVIAL(info) << warning;
// Simplify slices if required.
if (m_print->config().resolution)
this->simplify_slices(scale_(this->print()->config().resolution));
// Update bounding boxes, back up raw slices of complex models.
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
Layer &layer = *m_layers[layer_idx];
layer.lslices_bboxes.clear();
layer.lslices_bboxes.reserve(layer.lslices.size());
for (const ExPolygon &expoly : layer.lslices)
layer.lslices_bboxes.emplace_back(get_extents(expoly));
layer.backup_untyped_slices();
}
});
if (m_layers.empty())
throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
this->set_done(posSlice);
}
// 1) Merges typed region slices into stInternal type.
// 2) Increases an "extra perimeters" counter at region slices where needed.
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
@ -194,8 +148,8 @@ void PrintObject::make_perimeters()
[this, &region, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
LayerRegion &layerm = *m_layers[layer_idx]->m_regions[region_id];
const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->m_regions[region_id];
LayerRegion &layerm = *m_layers[layer_idx]->get_region(region_id);
const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->get_region(region_id);
const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices.surfaces);
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
@ -747,7 +701,6 @@ bool PrintObject::invalidate_all_steps()
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
// Then reset some of the depending values.
m_slicing_params.valid = false;
m_region_volumes.clear();
return result;
}
@ -1605,7 +1558,7 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr
}
}
PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders)
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders)
{
PrintRegionConfig config = default_region_config;
apply_to_print_region_config(config, volume.get_object()->config.get());
@ -1725,681 +1678,6 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
return updated;
}
// 1) Decides Z positions of the layers,
// 2) Initializes layers and their regions
// 3) Slices the object meshes
// 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes
// 5) Applies size compensation (offsets the slices in XY plane)
// 6) Replaces bad slices by the slices reconstructed from the upper/lower layer
// Resulting expolygons of layer regions are marked as Internal.
//
// this should be idempotent
void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
{
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
m_typed_slices = false;
// 1) Initialize layers and their slice heights.
std::vector<float> slice_zs;
{
this->clear_layers();
// Object layers (pairs of bottom/top Z coordinate), without the raft.
std::vector<coordf_t> object_layers = generate_object_layers(m_slicing_params, layer_height_profile);
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
int id = int(m_slicing_params.raft_layers());
slice_zs.reserve(object_layers.size());
Layer *prev = nullptr;
for (size_t i_layer = 0; i_layer < object_layers.size(); i_layer += 2) {
coordf_t lo = object_layers[i_layer];
coordf_t hi = object_layers[i_layer + 1];
coordf_t slice_z = 0.5 * (lo + hi);
Layer *layer = this->add_layer(id ++, hi - lo, hi + m_slicing_params.object_print_z_min, slice_z);
slice_zs.push_back(float(slice_z));
if (prev != nullptr) {
prev->upper_layer = layer;
layer->lower_layer = prev;
}
// Make sure all layers contain layer region objects for all regions.
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id)
layer->add_region(&this->printing_region(region_id));
prev = layer;
}
}
// Count model parts and modifier meshes, check whether the model parts are of the same region.
int all_volumes_single_region = -2; // not set yet
bool has_z_ranges = false;
size_t num_volumes = 0;
size_t num_modifiers = 0;
for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
int last_volume_id = -1;
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *model_volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (model_volume->is_model_part()) {
if (last_volume_id == volume_w_zrange.volume_idx) {
has_z_ranges = true;
} else {
last_volume_id = volume_w_zrange.volume_idx;
if (all_volumes_single_region == -2)
// first model volume met
all_volumes_single_region = region_id;
else if (all_volumes_single_region != region_id)
// multiple volumes met and they are not equal
all_volumes_single_region = -1;
++ num_volumes;
}
} else if (model_volume->is_modifier())
++ num_modifiers;
}
}
assert(num_volumes > 0);
// Slice all non-modifier volumes.
bool clipped = false;
bool upscaled = false;
bool spiral_vase = this->print()->config().spiral_vase;
auto slicing_mode = spiral_vase ? MeshSlicingParams::SlicingMode::PositiveLargestContour : MeshSlicingParams::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 < m_region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - region " << region_id;
// slicing in parallel
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->printing_region(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, MeshSlicingParams::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)
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons_by_layer[layer_id]), stInternal);
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - append slices " << region_id << " end";
}
} else {
// Expensive path: Slice one volume after the other in the order they are presented at the user interface,
// clip the last volumes with the first.
// First slice the volumes.
struct SlicedVolume {
SlicedVolume(int volume_id, int region_id, std::vector<ExPolygons> &&expolygons_by_layer) :
volume_id(volume_id), region_id(region_id), expolygons_by_layer(std::move(expolygons_by_layer)) {}
int volume_id;
int region_id;
std::vector<ExPolygons> expolygons_by_layer;
};
std::vector<SlicedVolume> sliced_volumes;
sliced_volumes.reserve(num_volumes);
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_model_part()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - volume " << volume_id;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1;
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
else
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
// slicing in parallel
sliced_volumes.emplace_back(volume_id, (int)region_id, this->slice_volume(slice_zs, ranges, slicing_mode, *model_volume));
i = j;
} else
++ i;
}
}
// Second clip the volumes in the order they are presented at the user interface.
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, slice_zs.size()),
[this, &sliced_volumes, num_modifiers](const tbb::blocked_range<size_t>& range) {
float delta = float(scale_(m_config.xy_size_compensation.value));
// Only upscale together with clipping if there are no modifiers, as the modifiers shall be applied before upscaling
// (upscaling may grow the object outside of the modifier mesh).
bool upscale = delta > 0 && num_modifiers == 0;
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
m_print->throw_if_canceled();
// Trim volumes in a single layer, one by the other, possibly apply upscaling.
{
Polygons processed;
for (SlicedVolume &sliced_volume : sliced_volumes)
if (! sliced_volume.expolygons_by_layer.empty()) {
ExPolygons slices = std::move(sliced_volume.expolygons_by_layer[layer_id]);
if (upscale)
slices = offset_ex(std::move(slices), delta);
if (! processed.empty())
// Trim by the slices of already processed regions.
slices = diff_ex(slices, processed);
if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size())
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
sliced_volume.expolygons_by_layer[layer_id] = std::move(slices);
}
}
// Collect and union volumes of a single region.
for (int region_id = 0; region_id < int(m_region_volumes.size()); ++ region_id) {
ExPolygons expolygons;
size_t num_volumes = 0;
for (SlicedVolume &sliced_volume : sliced_volumes)
if (sliced_volume.region_id == region_id && ! sliced_volume.expolygons_by_layer.empty() && ! sliced_volume.expolygons_by_layer[layer_id].empty()) {
++ num_volumes;
append(expolygons, std::move(sliced_volume.expolygons_by_layer[layer_id]));
}
if (num_volumes > 1)
// Merge the islands using a positive / negative offset.
expolygons = offset_ex(offset_ex(expolygons, float(scale_(EPSILON))), -float(scale_(EPSILON)));
m_layers[layer_id]->regions()[region_id]->slices.append(std::move(expolygons), stInternal);
}
}
});
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - parallel clipping - end";
clipped = true;
upscaled = m_config.xy_size_compensation.value > 0 && num_modifiers == 0;
}
// Slice all modifier volumes.
if (m_region_volumes.size() > 1) {
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - region " << region_id;
// slicing in parallel
std::vector<ExPolygons> expolygons_by_layer = this->slice_modifiers(region_id, slice_zs);
m_print->throw_if_canceled();
if (expolygons_by_layer.empty())
continue;
// loop through the other regions and 'steal' the slices belonging to this one
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &expolygons_by_layer, region_id](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
for (size_t other_region_id = 0; other_region_id < m_region_volumes.size(); ++ other_region_id) {
if (region_id == other_region_id)
continue;
Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty())
continue;
ExPolygons my_parts = intersection_ex(other_layerm->slices.surfaces, expolygons_by_layer[layer_id]);
if (my_parts.empty())
continue;
// Remove such parts from original region.
other_layerm->slices.set(diff_ex(other_layerm->slices.surfaces, my_parts), stInternal);
// Append new parts to our region.
layerm->slices.append(std::move(my_parts), stInternal);
}
}
});
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing modifier volumes - stealing " << region_id << " end";
}
}
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - removing top empty layers";
while (! m_layers.empty()) {
const Layer *layer = m_layers.back();
if (! layer->empty())
goto end;
delete layer;
m_layers.pop_back();
if (! m_layers.empty())
m_layers.back()->upper_layer = nullptr;
}
m_print->throw_if_canceled();
end:
;
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - begin";
{
// Compensation value, scaled.
const float xy_compensation_scaled = float(scale_(m_config.xy_size_compensation.value));
const float elephant_foot_compensation_scaled = (m_config.raft_layers == 0) ?
// Only enable Elephant foot compensation if printing directly on the print bed.
float(scale_(m_config.elefant_foot_compensation.value)) :
0.f;
// Uncompensated slices for the first layer in case the Elephant foot compensation is applied.
ExPolygons lslices_1st_layer;
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, upscaled, clipped, xy_compensation_scaled, elephant_foot_compensation_scaled, &lslices_1st_layer]
(const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
m_print->throw_if_canceled();
Layer *layer = m_layers[layer_id];
// Apply size compensation and perform clipping of multi-part objects.
float elfoot = (layer_id == 0) ? elephant_foot_compensation_scaled : 0.f;
if (layer->m_regions.size() == 1) {
assert(! upscaled);
assert(! clipped);
// Optimized version for a single region layer.
// Single region, growing or shrinking.
LayerRegion *layerm = layer->m_regions.front();
if (elfoot > 0) {
// Apply the elephant foot compensation and store the 1st layer slices without the Elephant foot compensation applied.
lslices_1st_layer = to_expolygons(std::move(layerm->slices.surfaces));
float delta = xy_compensation_scaled;
if (delta > elfoot) {
delta -= elfoot;
elfoot = 0.f;
} else if (delta > 0)
elfoot -= delta;
layerm->slices.set(
union_ex(
Slic3r::elephant_foot_compensation(
(delta == 0.f) ? lslices_1st_layer : offset_ex(lslices_1st_layer, delta),
layerm->flow(frExternalPerimeter), unscale<double>(elfoot))),
stInternal);
if (xy_compensation_scaled != 0.f)
lslices_1st_layer = offset_ex(std::move(lslices_1st_layer), xy_compensation_scaled);
} else if (xy_compensation_scaled != 0.f) {
// Apply the XY compensation.
layerm->slices.set(
offset_ex(to_expolygons(std::move(layerm->slices.surfaces)), xy_compensation_scaled),
stInternal);
}
} else {
bool upscale = ! upscaled && xy_compensation_scaled > 0.f;
bool clip = ! clipped && m_config.clip_multipart_objects.value;
if (upscale || clip) {
// Multiple regions, growing or just clipping one region by the other.
// When clipping the regions, priority is given to the first regions.
Polygons processed;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
ExPolygons slices = to_expolygons(std::move(layerm->slices.surfaces));
if (upscale)
slices = offset_ex(std::move(slices), xy_compensation_scaled);
if (region_id > 0 && clip)
// Trim by the slices of already processed regions.
slices = diff_ex(slices, processed);
if (clip && (region_id + 1 < layer->m_regions.size()))
// Collect the already processed regions to trim the to be processed regions.
polygons_append(processed, slices);
layerm->slices.set(std::move(slices), stInternal);
}
}
if (xy_compensation_scaled < 0.f || elfoot > 0.f) {
// Apply the negative XY compensation.
Polygons trimming;
static const float eps = float(scale_(m_config.slice_closing_radius.value) * 1.5);
if (elfoot > 0.f) {
lslices_1st_layer = offset_ex(layer->merged(eps), std::min(xy_compensation_scaled, 0.f) - eps);
trimming = to_polygons(Slic3r::elephant_foot_compensation(lslices_1st_layer,
layer->m_regions.front()->flow(frExternalPerimeter), unscale<double>(elfoot)));
} else
trimming = offset(layer->merged(float(SCALED_EPSILON)), xy_compensation_scaled - float(SCALED_EPSILON));
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id)
layer->m_regions[region_id]->trim_surfaces(trimming);
}
}
// Merge all regions' slices to get islands, chain them by a shortest path.
layer->make_slices();
}
});
if (elephant_foot_compensation_scaled > 0.f && ! m_layers.empty()) {
// The Elephant foot has been compensated, therefore the 1st layer's lslices are shrank with the Elephant foot compensation value.
// Store the uncompensated value there.
assert(m_layers.front()->id() == 0);
m_layers.front()->lslices = std::move(lslices_1st_layer);
}
}
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - make_slices in parallel - 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<ExPolygons> PrintObject::slice_region(size_t region_id, const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below) const
{
std::vector<const ModelVolume*> volumes;
if (region_id < m_region_volumes.size()) {
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (volume->is_model_part())
volumes.emplace_back(volume);
}
}
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_w_zrange at most once.
std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std::vector<float> &slice_zs) const
{
std::vector<ExPolygons> out;
if (region_id < m_region_volumes.size())
{
std::vector<std::vector<t_layer_height_range>> volume_ranges;
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
volume_ranges.reserve(volumes_and_ranges.volumes.size());
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) {
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1;
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j) {
if (! ranges.empty() && std::abs(ranges.back().second - volumes_and_ranges.volumes[j].layer_height_range.first) < EPSILON)
ranges.back().second = volumes_and_ranges.volumes[j].layer_height_range.second;
else
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
}
volume_ranges.emplace_back(std::move(ranges));
i = j;
} else
++ i;
}
if (! volume_ranges.empty())
{
bool equal_ranges = true;
for (size_t i = 1; i < volume_ranges.size(); ++ i) {
assert(! volume_ranges[i].empty());
if (volume_ranges.front() != volume_ranges[i]) {
equal_ranges = false;
break;
}
}
if (equal_ranges && volume_ranges.front().size() == 1 && volume_ranges.front().front() == t_layer_height_range(0, DBL_MAX)) {
// No modifier in this region was split to layer spans.
std::vector<const ModelVolume*> volumes;
for (const PrintRegionVolumes::VolumeWithZRange &volume_w_zrange : m_region_volumes[region_id].volumes) {
const ModelVolume *volume = this->model_object()->volumes[volume_w_zrange.volume_idx];
if (volume->is_modifier())
volumes.emplace_back(volume);
}
out = this->slice_volumes(slice_zs, MeshSlicingParams::SlicingMode::Regular, volumes);
} else {
// Some modifier in this region was split to layer spans.
std::vector<char> merge;
for (size_t region_id = 0; region_id < m_region_volumes.size(); ++ region_id) {
const PrintRegionVolumes &volumes_and_ranges = m_region_volumes[region_id];
for (size_t i = 0; i < volumes_and_ranges.volumes.size(); ) {
int volume_id = volumes_and_ranges.volumes[i].volume_idx;
const ModelVolume *model_volume = this->model_object()->volumes[volume_id];
if (model_volume->is_modifier()) {
BOOST_LOG_TRIVIAL(debug) << "Slicing modifiers - volume " << volume_id;
// Find the ranges of this volume. Ranges in volumes_and_ranges must not overlap for a single volume.
std::vector<t_layer_height_range> ranges;
ranges.emplace_back(volumes_and_ranges.volumes[i].layer_height_range);
size_t j = i + 1;
for (; j < volumes_and_ranges.volumes.size() && volume_id == volumes_and_ranges.volumes[j].volume_idx; ++ j)
ranges.emplace_back(volumes_and_ranges.volumes[j].layer_height_range);
// slicing in parallel
std::vector<ExPolygons> this_slices = this->slice_volume(slice_zs, ranges, MeshSlicingParams::SlicingMode::Regular, *model_volume);
// Variable this_slices could be empty if no value of slice_zs is within any of the ranges of this volume.
if (out.empty()) {
out = std::move(this_slices);
merge.assign(out.size(), false);
} else if (!this_slices.empty()) {
assert(out.size() == this_slices.size());
for (size_t i = 0; i < out.size(); ++ i)
if (! this_slices[i].empty()) {
if (! out[i].empty()) {
append(out[i], this_slices[i]);
merge[i] = true;
} else
out[i] = std::move(this_slices[i]);
}
}
i = j;
} else
++ i;
}
}
for (size_t i = 0; i < merge.size(); ++ i)
if (merge[i])
out[i] = union_ex(out[i]);
}
}
}
return out;
}
std::vector<ExPolygons> PrintObject::slice_support_volumes(const ModelVolumeType &model_volume_type) const
{
std::vector<const ModelVolume*> volumes;
for (const ModelVolume *volume : this->model_object()->volumes)
if (volume->type() == model_volume_type)
volumes.emplace_back(volume);
std::vector<float> zs;
zs.reserve(this->layers().size());
for (const Layer *l : this->layers())
zs.emplace_back((float)l->slice_z);
return this->slice_volumes(zs, MeshSlicingParams::SlicingMode::Regular, volumes);
}
//FIXME The admesh repair function may break the face connectivity, rather refresh it here as the slicing code relies on it.
static void fix_mesh_connectivity(TriangleMesh &mesh)
{
auto nr_degenerated = mesh.stl.stats.degenerate_facets;
stl_check_facets_exact(&mesh.stl);
if (nr_degenerated != mesh.stl.stats.degenerate_facets)
// stl_check_facets_exact() removed some newly degenerated faces. Some faces could become degenerate after some mesh transformation.
stl_generate_shared_vertices(&mesh.stl, mesh.its);
}
std::vector<ExPolygons> PrintObject::slice_volumes(
const std::vector<float> &z,
MeshSlicingParams::SlicingMode mode, size_t slicing_mode_normal_below_layer, MeshSlicingParams::SlicingMode mode_below,
const std::vector<const ModelVolume*> &volumes) const
{
std::vector<ExPolygons> layers;
if (! volumes.empty()) {
// Compose mesh.
//FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volumes.front()->mesh());
mesh.transform(volumes.front()->get_matrix(), true);
assert(mesh.repaired);
if (volumes.size() == 1 && mesh.repaired)
fix_mesh_connectivity(mesh);
for (size_t idx_volume = 1; idx_volume < volumes.size(); ++ idx_volume) {
const ModelVolume &model_volume = *volumes[idx_volume];
TriangleMesh vol_mesh(model_volume.mesh());
vol_mesh.transform(model_volume.get_matrix(), true);
mesh.merge(vol_mesh);
mesh.repair(false);
}
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true);
// apply XY shift
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing
const Print *print = this->print();
// TriangleMeshSlicer needs shared vertices, also this calls the repair() function.
mesh.require_shared_vertices();
MeshSlicingParamsEx params { { mode, slicing_mode_normal_below_layer, mode_below }, float(m_config.slice_closing_radius.value) };
layers = slice_mesh_ex(mesh.its, z, params, [print]() { print->throw_if_canceled(); });
m_print->throw_if_canceled();
}
}
return layers;
}
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const
{
std::vector<ExPolygons> layers;
if (! z.empty()) {
// Compose mesh.
//FIXME better to split the mesh into separate shells, perform slicing over each shell separately and then to use a Boolean operation to merge them.
TriangleMesh mesh(volume.mesh());
mesh.transform(volume.get_matrix(), true);
if (mesh.repaired)
fix_mesh_connectivity(mesh);
if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true);
// apply XY shift
mesh.translate(- unscale<float>(m_center_offset.x()), - unscale<float>(m_center_offset.y()), 0);
// perform actual slicing
const Print *print = this->print();
// TriangleMeshSlicer needs the shared vertices.
mesh.require_shared_vertices();
MeshSlicingParamsEx params;
params.mode = mode;
params.closing_radius = float(m_config.slice_closing_radius.value);
layers = slice_mesh_ex(mesh.its, z, params, [print](){ print->throw_if_canceled(); });
m_print->throw_if_canceled();
}
}
return layers;
}
// Filter the zs not inside the ranges. The ranges are closed at the bottom and open at the top, they are sorted lexicographically and non overlapping.
std::vector<ExPolygons> PrintObject::slice_volume(const std::vector<float> &z, const std::vector<t_layer_height_range> &ranges, MeshSlicingParams::SlicingMode mode, const ModelVolume &volume) const
{
std::vector<ExPolygons> 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, mode, volume);
} else {
std::vector<float> z_filtered;
std::vector<std::pair<size_t, size_t>> n_filtered;
z_filtered.reserve(z.size());
n_filtered.reserve(2 * ranges.size());
size_t i = 0;
for (const t_layer_height_range &range : ranges) {
for (; i < z.size() && z[i] < range.first; ++ i) ;
size_t first = i;
for (; i < z.size() && z[i] < range.second; ++ i)
z_filtered.emplace_back(z[i]);
if (i > first)
n_filtered.emplace_back(std::make_pair(first, i));
}
if (! n_filtered.empty()) {
std::vector<ExPolygons> layers = this->slice_volume(z_filtered, mode, volume);
out.assign(z.size(), ExPolygons());
i = 0;
for (const std::pair<size_t, size_t> &span : n_filtered)
for (size_t j = span.first; j < span.second; ++ j)
out[j] = std::move(layers[i ++]);
}
}
}
return out;
}
std::string PrintObject::_fix_slicing_errors()
{
// Collect layers with slicing errors.
// These layers will be fixed in parallel.
std::vector<size_t> buggy_layers;
buggy_layers.reserve(m_layers.size());
for (size_t idx_layer = 0; idx_layer < m_layers.size(); ++ idx_layer)
if (m_layers[idx_layer]->slicing_errors)
buggy_layers.push_back(idx_layer);
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - begin";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, buggy_layers.size()),
[this, &buggy_layers](const tbb::blocked_range<size_t>& range) {
for (size_t buggy_layer_idx = range.begin(); buggy_layer_idx < range.end(); ++ buggy_layer_idx) {
m_print->throw_if_canceled();
size_t idx_layer = buggy_layers[buggy_layer_idx];
Layer *layer = m_layers[idx_layer];
assert(layer->slicing_errors);
// Try to repair the layer surfaces by merging all contours and all holes from neighbor layers.
// BOOST_LOG_TRIVIAL(trace) << "Attempting to repair layer" << idx_layer;
for (size_t region_id = 0; region_id < layer->m_regions.size(); ++ region_id) {
LayerRegion *layerm = layer->m_regions[region_id];
// Find the first valid layer below / above the current layer.
const Surfaces *upper_surfaces = nullptr;
const Surfaces *lower_surfaces = nullptr;
for (size_t j = idx_layer + 1; j < m_layers.size(); ++ j)
if (! m_layers[j]->slicing_errors) {
upper_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces;
break;
}
for (int j = int(idx_layer) - 1; j >= 0; -- j)
if (! m_layers[j]->slicing_errors) {
lower_surfaces = &m_layers[j]->regions()[region_id]->slices.surfaces;
break;
}
// Collect outer contours and holes from the valid layers above & below.
Polygons outer;
outer.reserve(
((upper_surfaces == nullptr) ? 0 : upper_surfaces->size()) +
((lower_surfaces == nullptr) ? 0 : lower_surfaces->size()));
size_t num_holes = 0;
if (upper_surfaces)
for (const auto &surface : *upper_surfaces) {
outer.push_back(surface.expolygon.contour);
num_holes += surface.expolygon.holes.size();
}
if (lower_surfaces)
for (const auto &surface : *lower_surfaces) {
outer.push_back(surface.expolygon.contour);
num_holes += surface.expolygon.holes.size();
}
Polygons holes;
holes.reserve(num_holes);
if (upper_surfaces)
for (const auto &surface : *upper_surfaces)
polygons_append(holes, surface.expolygon.holes);
if (lower_surfaces)
for (const auto &surface : *lower_surfaces)
polygons_append(holes, surface.expolygon.holes);
layerm->slices.set(diff_ex(union_(outer), holes), stInternal);
}
// Update layer slices after repairing the single regions.
layer->make_slices();
}
});
m_print->throw_if_canceled();
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - fixing slicing errors in parallel - end";
// remove empty layers from bottom
while (! m_layers.empty() && (m_layers.front()->lslices.empty() || m_layers.front()->empty())) {
delete m_layers.front();
m_layers.erase(m_layers.begin());
m_layers.front()->lower_layer = nullptr;
for (size_t i = 0; i < m_layers.size(); ++ i)
m_layers[i]->set_id(m_layers[i]->id() - 1);
}
return buggy_layers.empty() ? "" :
"The model has overlapping or self-intersecting facets. I tried to repair it, "
"however you might want to check the results or repair the input file and retry.\n";
}
// Simplify the sliced model, if "resolution" configuration parameter > 0.
// The simplification is problematic, because it simplifies the slices independent from each other,
// which makes the simplified discretization visible on the object surface.
void PrintObject::simplify_slices(double distance)
{
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - begin";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()),
[this, distance](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled();
Layer *layer = m_layers[layer_idx];
for (size_t region_idx = 0; region_idx < layer->m_regions.size(); ++ region_idx)
layer->m_regions[region_idx]->slices.simplify(distance);
{
ExPolygons simplified;
for (const ExPolygon &expoly : layer->lslices)
expoly.simplify(distance, &simplified);
layer->lslices = std::move(simplified);
}
}
});
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end";
}
// Only active if config->infill_only_where_needed. This step trims the sparse infill,
// so it acts as an internal support. It maintains all other infill types intact.
// Here the internal surfaces and perimeters have to be supported by the sparse infill.