Support generator parallelized.

This commit is contained in:
bubnikv 2017-03-22 15:35:50 +01:00
parent 04cd474708
commit ed495663e8
2 changed files with 1080 additions and 910 deletions

View File

@ -12,10 +12,18 @@
#include <memory>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
#include <tbb/atomic.h>
#include <tbb/spin_mutex.h>
#include <tbb/task_group.h>
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
//#ifdef SLIC3R_DEBUG
#if 1
#define DEBUG
#define _DEBUG
#undef NDEBUG
#include "SVG.hpp"
#endif
@ -193,6 +201,19 @@ inline PrintObjectSupportMaterial::MyLayer& layer_allocate(
return layer_storage.back();
}
inline PrintObjectSupportMaterial::MyLayer& layer_allocate(
std::deque<PrintObjectSupportMaterial::MyLayer> &layer_storage,
tbb::spin_mutex &layer_storage_mutex,
PrintObjectSupportMaterial::SupporLayerType layer_type)
{
layer_storage_mutex.lock();
layer_storage.push_back(PrintObjectSupportMaterial::MyLayer());
PrintObjectSupportMaterial::MyLayer *layer_new = &layer_storage.back();
layer_storage_mutex.unlock();
layer_new->layer_type = layer_type;
return *layer_new;
}
inline void layers_append(PrintObjectSupportMaterial::MyLayersPtr &dst, const PrintObjectSupportMaterial::MyLayersPtr &src)
{
dst.insert(dst.end(), src.begin(), src.end());
@ -266,7 +287,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// For example, a single nozzle multi material printing will need to generate a waste tower, which in turn
// wastes less material, if there are as little tool changes as possible.
MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers(
object, bottom_contacts, top_contacts, layer_storage, max_object_layer_height);
object, bottom_contacts, top_contacts, layer_storage);
this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
@ -295,14 +316,14 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// Propagate top / bottom contact layers to generate interface layers.
MyLayersPtr interface_layers = this->generate_interface_layers(
object, bottom_contacts, top_contacts, intermediate_layers, layer_storage);
bottom_contacts, top_contacts, intermediate_layers, layer_storage);
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating raft";
// If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled.
// There is also a 1st intermediate layer containing bases of support columns.
// Inflate the bases of the support columns and create the raft base under the object.
MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, interface_layers, intermediate_layers, layer_storage);
MyLayersPtr raft_layers = this->generate_raft_base(top_contacts, interface_layers, intermediate_layers, layer_storage);
#ifdef SLIC3R_DEBUG
for (MyLayersPtr::const_iterator it = interface_layers.begin(); it != interface_layers.end(); ++ it)
@ -465,23 +486,44 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// If user specified a custom angle threshold, convert it to radians.
// Zero means automatic overhang detection.
double threshold_rad = (m_object_config->support_material_threshold.value > 0) ?
const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ?
M_PI * double(m_object_config->support_material_threshold.value + 1) / 180. : // +1 makes the threshold inclusive
0.;
// Build support on a build plate only? If so, then collect top surfaces into buildplate_only_top_surfaces
// and subtract buildplate_only_top_surfaces from the contact surfaces, so
// there is no contact surface supported by a top surface.
bool buildplate_only = this->build_plate_only();
Polygons buildplate_only_top_surfaces;
// Build support on a build plate only? If so, then collect and union all the surfaces below the current layer.
// Unfortunately this is an inherently serial process.
//FIXME this may be parallelized to some extent by summing the polygons by multiples of layers, but it may be counter-productive.
const bool buildplate_only = this->build_plate_only();
std::vector<Polygons> buildplate_covered;
if (buildplate_only) {
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() - collecting regions covering the print bed.";
buildplate_covered.assign(object.layers.size(), Polygons());
for (size_t layer_id = 1; layer_id < object.layers.size(); ++ layer_id) {
const Layer &lower_layer = *object.layers[layer_id-1];
// Merge the new slices with the preceding slices.
// Apply the safety offset to the newly added polygons, so they will connect
// with the polygons collected before,
// but don't apply the safety offset during the union operation as it would
// inflate the polygons over and over.
Polygons &covered = buildplate_covered[layer_id];
covered = buildplate_covered[layer_id - 1];
polygons_append(covered, offset(lower_layer.slices.expolygons, scale_(0.01)));
covered = union_(covered, false); // don't apply the safety offset.
}
}
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - start";
// Determine top contact areas.
// If generating raft only (no support), only calculate top contact areas for the 0th layer.
size_t num_layers = this->has_support() ? object.layer_count() : 1;
// If having a raft, start with 0th layer, otherwise with 1st layer.
// Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
// So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
for (size_t layer_id = this->has_raft() ? 0 : 1; layer_id < num_layers; ++ layer_id)
size_t num_layers = this->has_support() ? object.layer_count() : 1;
contact_out.assign(num_layers, nullptr);
tbb::spin_mutex layer_storage_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
[this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id)
{
const Layer &layer = *object.layers[layer_id];
@ -501,23 +543,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
} else {
// Generate overhang / contact_polygons for non-raft layers.
const Layer &lower_layer = *object.layers[layer_id-1];
if (buildplate_only) {
// Merge the new slices with the preceding slices.
// Apply the safety offset to the newly added polygons, so they will connect
// with the polygons collected before,
// but don't apply the safety offset during the union operation as it would
// inflate the polygons over and over.
polygons_append(buildplate_only_top_surfaces, offset(lower_layer.slices.expolygons, scale_(0.01)));
buildplate_only_top_surfaces = union_(buildplate_only_top_surfaces, false); // don't apply the safety offset.
}
for (LayerRegionPtrs::const_iterator it_layerm = layer.regions.begin(); it_layerm != layer.regions.end(); ++ it_layerm) {
const LayerRegion &layerm = *(*it_layerm);
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
float fw = float(layerm.flow(frExternalPerimeter).scaled_width());
float lower_layer_offset =
(layer_id < m_object_config->support_material_enforce_layers.value) ?
(layer_id < this->m_object_config->support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
0.f :
(threshold_rad > 0. ?
@ -557,7 +589,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
}
#endif /* SLIC3R_DEBUG */
if (m_object_config->dont_support_bridges) {
if (this->m_object_config->dont_support_bridges) {
// compute the area of bridging perimeters
// Note: this is duplicate code from GCode.pm, we need to refactor
if (true) {
@ -625,10 +657,10 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
}
} // if (m_objconfig->dont_support_bridges)
if (buildplate_only) {
if (! buildplate_covered.empty()) {
// Don't support overhangs above the top surfaces.
// This step is done before the contact surface is calculated by growing the overhang region.
diff_polygons = diff(diff_polygons, buildplate_only_top_surfaces);
diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
}
if (diff_polygons.empty())
@ -653,9 +685,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
if (slices_margin_cached_offset != slices_margin_offset) {
slices_margin_cached_offset = slices_margin_offset;
slices_margin_cached = offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (buildplate_only) {
if (! buildplate_covered.empty()) {
// Trim the inflated contact surfaces by the top surfaces as well.
polygons_append(slices_margin_cached, buildplate_only_top_surfaces);
polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
slices_margin_cached = union_(slices_margin_cached);
}
}
@ -678,7 +710,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// now apply the contact areas to the layer were they need to be made
if (! contact_polygons.empty()) {
// get the average nozzle diameter used on this layer
MyLayer &new_layer = layer_allocate(layer_storage, sltTopContact);
MyLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
const Layer *layer_below = (layer_id > 0) ? object.layers[layer_id - 1] : NULL;
new_layer.idx_object_layer_above = layer_id;
if (m_slicing_params.soluble_interface) {
@ -789,17 +821,20 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// The overhang polygons are used in the path generator for planning of the contact loops.
// if (this->has_contact_loops())
new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
contact_out.push_back(&new_layer);
if (0) {
contact_out[layer_id] = &new_layer;
#if 0
// Slic3r::SVG::output("out\\contact_" . $contact_z . ".svg",
// green_expolygons => union_ex($buildplate_only_top_surfaces),
// blue_expolygons => union_ex(\@contact),
// red_expolygons => union_ex(\@overhang),
// );
#endif
}
}
}
});
// Compress contact_out, remove the nullptr items.
remove_nulls(contact_out);
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";
return contact_out;
}
@ -833,10 +868,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) {
BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
const Layer &layer = *object.get_layer(layer_id);
// Top surfaces of this layer, to be used to stop the surface volume from growing down.
Polygons top;
if (! m_object_config->support_material_buildplate_only)
top = collect_region_slices_by_type(layer, stTop);
// Collect projections of all contact areas above or at the same level as this top surface.
for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
Polygons polygons_new;
@ -856,15 +887,21 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
}
if (projection.empty())
continue;
projection = union_(projection);
Polygons projection_raw = union_(projection);
// Top surfaces of this layer, to be used to stop the surface volume from growing down.
tbb::task_group task_group;
if (! m_object_config->support_material_buildplate_only)
task_group.run([this, &object, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] {
Polygons top = collect_region_slices_by_type(layer, stTop);
#ifdef SLIC3R_DEBUG
{
BoundingBox bbox = get_extents(projection);
BoundingBox bbox = get_extents(projection_raw);
bbox.merge(get_extents(top));
::Slic3r::SVG svg(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), bbox);
svg.draw(union_ex(top, false), "blue", 0.5f);
svg.draw(union_ex(projection, true), "red", 0.5f);
svg.draw_outline(union_ex(projection, true), "red", "blue", scale_(0.1f));
svg.draw(union_ex(projection_raw, true), "red", 0.5f);
svg.draw_outline(union_ex(projection_raw, true), "red", "blue", scale_(0.1f));
svg.draw(layer.slices.expolygons, "green", 0.5f);
}
#endif /* SLIC3R_DEBUG */
@ -873,9 +910,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// top surfaces above layer.print_z falls onto this top surface.
// Touching are the contact surfaces supported exclusively by this top surfaces.
// Don't use a safety offset as it has been applied during insertion of polygons.
Polygons touching;
if (! top.empty()) {
touching = intersection(top, projection, false);
Polygons touching = intersection(top, projection_raw, false);
if (! touching.empty()) {
// Allocate a new bottom contact layer.
MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact);
@ -911,19 +947,21 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
}
}
} // ! top.empty()
});
Polygons &layer_support_area = layer_support_areas[layer_id];
task_group.run([this, &projection, &projection_raw, &layer, &layer_support_area, layer_id] {
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
// Polygons trimming = union_(to_polygons(layer.slices.expolygons), touching, true);
Polygons trimming = offset(layer.slices.expolygons, float(SCALED_EPSILON));
projection = diff(projection, trimming, false);
projection = diff(projection_raw, trimming, false);
remove_sticks(projection);
remove_degenerate(projection);
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
Slic3r::EdgeGrid::Grid grid;
coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing();
coord_t grid_resolution = scale_(support_spacing); // scale_(1.5f);
coord_t grid_resolution = coord_t(scale_(support_spacing));
BoundingBox bbox = get_extents(projection);
bbox.offset(20);
bbox.align_to_grid(grid_resolution);
@ -931,6 +969,17 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
grid.create(projection, grid_resolution);
grid.calculate_sdf();
tbb::task_group task_group_inner;
// Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing
// to allow a placement of suppot zig-zag snake along the grid lines.
task_group_inner.run([this, &grid, &trimming, &layer_support_area] {
layer_support_area = diff(
grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 25),
trimming,
false);
});
task_group_inner.run([&projection, &grid, &trimming, layer_id] {
// Extract a bounding contour from the grid.
Polygons projection_simplified = grid.contours_simplified(-5);
#ifdef SLIC3R_DEBUG
@ -949,39 +998,99 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
#endif /* SLIC3R_GUI */
}
#endif /* SLIC3R_DEBUG */
// Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing
// to allow a placement of suppot zig-zag snake along the grid lines.
layer_support_areas[layer_id] = diff(
grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 25),
trimming,
false);
// Trim the base layer by the object layer.
projection = diff(projection_simplified, trimming, false);
});
task_group_inner.wait();
});
task_group.wait();
}
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
} // ! top_contacts.empty()
trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
return bottom_contacts;
}
// FN_HIGHER_EQUAL: the provided object pointer has a Z value >= of an internal threshold.
// Find the first item with Z value >= of an internal threshold of fn_higher_equal.
// If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size()
// If the initial idx is size_t(-1), then use binary search.
// Otherwise search linearly upwards.
template<typename T, typename FN_HIGHER_EQUAL>
size_t idx_higher_or_equal(const std::vector<T*> &vec, size_t idx, FN_HIGHER_EQUAL fn_higher_equal)
{
if (vec.empty()) {
idx = 0;
} else if (idx == size_t(-1)) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, int(vec.size()) - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_higher_equal(vec[idx_mid]))
idx_high = idx_mid;
else
idx_low = idx_mid;
}
idx = fn_higher_equal(vec[idx_low]) ? idx_low :
(fn_higher_equal(vec[idx_high]) ? idx_high : vec.size());
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (idx < vec.size() && ! fn_higher_equal(vec[idx]))
++ idx;
}
return idx;
}
// FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold.
// Find the first item with Z value <= of an internal threshold of fn_lower_equal.
// If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1.
// If the initial idx is < -1, then use binary search.
// Otherwise search linearly downwards.
template<typename T, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lower_equal)
{
if (vec.empty()) {
idx = -1;
} else if (idx < -1) {
// First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0;
int idx_high = std::max(0, int(vec.size()) - 1);
while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2;
if (fn_lower_equal(vec[idx_mid]))
idx_low = idx_mid;
else
idx_high = idx_mid;
}
idx = fn_lower_equal(vec[idx_high]) ? idx_high :
(fn_lower_equal(vec[idx_low ]) ? idx_low : -1);
} else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search.
while (idx >= 0 && ! fn_lower_equal(vec[idx]))
-- idx;
}
return idx;
}
// Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them.
void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts(
const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const
{
size_t idx_top_first = 0;
// For all bottom contact layers:
for (size_t idx_bottom = 0; idx_bottom < bottom_contacts.size() && idx_top_first < top_contacts.size(); ++ idx_bottom) {
const MyLayer &layer_bottom = *bottom_contacts[idx_bottom];
// Find the first top layer overlapping with layer_bottom.
while (idx_top_first < top_contacts.size() && top_contacts[idx_top_first]->bottom_z < layer_bottom.bottom_print_z() - EPSILON)
++ idx_top_first;
// For all top contact layers overlapping with the thick bottom contact layer:
for (size_t idx_top = idx_top_first; idx_top < top_contacts.size(); ++ idx_top) {
tbb::parallel_for(tbb::blocked_range<int>(0, int(top_contacts.size())),
[this, &object, &bottom_contacts, &top_contacts](const tbb::blocked_range<int>& range) {
int idx_bottom_overlapping_first = -2;
// For all top contact layers, counting downwards due to the way idx_higher_or_equal caches the last index to avoid repeated binary search.
for (int idx_top = range.end() - 1; idx_top >= range.begin(); -- idx_top) {
MyLayer &layer_top = *top_contacts[idx_top];
assert(layer_top.bottom_z >= layer_bottom.bottom_print_z() - EPSILON);
// Find the first bottom layer overlapping with layer_top.
idx_bottom_overlapping_first = idx_lower_or_equal(bottom_contacts, idx_bottom_overlapping_first, [&layer_top](const MyLayer *layer_bottom){ return layer_bottom->bottom_print_z() - EPSILON <= layer_top.bottom_z; });
// For all top contact layers overlapping with the thick bottom contact layer:
for (int idx_bottom_overlapping = idx_bottom_overlapping_first; idx_bottom_overlapping >= 0; -- idx_bottom_overlapping) {
const MyLayer &layer_bottom = *bottom_contacts[idx_bottom_overlapping];
assert(layer_bottom.bottom_print_z() - EPSILON <= layer_top.bottom_z);
if (layer_top.print_z < layer_bottom.print_z + EPSILON) {
// Layers overlap. Trim layer_top with layer_bottom.
layer_top.polygons = diff(layer_top.polygons, layer_bottom.polygons);
@ -989,6 +1098,7 @@ void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts(
break;
}
}
});
}
// A helper for sorting the top / bottom contact layers by their contact with the touching support layer:
@ -1010,8 +1120,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayerStorage &layer_storage,
const coordf_t max_object_layer_height) const
MyLayerStorage &layer_storage) const
{
MyLayersPtr intermediate_layers;
@ -1145,7 +1254,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
std::vector<Polygons> &layer_support_areas) const
const std::vector<Polygons> &layer_support_areas) const
{
#ifdef SLIC3R_DEBUG
static int iRun = 0;
@ -1156,10 +1265,17 @@ void PrintObjectSupportMaterial::generate_base_layers(
return;
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
int idx_top_contact_above = int(top_contacts.size()) - 1;
int idx_bottom_contact_overlapping = int(bottom_contacts.size()) - 1;
int idx_object_layer_above = int(object.total_layer_count()) - 1;
for (int idx_intermediate = int(intermediate_layers.size()) - 1; idx_intermediate >= 0; -- idx_intermediate)
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, intermediate_layers.size()),
[this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_support_areas](const tbb::blocked_range<size_t>& range) {
// index -2 means not initialized yet, -1 means intialized and decremented to 0 and then -1.
int idx_top_contact_above = -2;
int idx_bottom_contact_overlapping = -2;
int idx_object_layer_above = -2;
// Counting down due to the way idx_lower_or_equal caches indices to avoid repeated binary search over the complete sequence.
for (int idx_intermediate = int(range.end()) - 1; idx_intermediate >= int(range.begin()); -- idx_intermediate)
{
BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " <<
idx_intermediate << " of " << intermediate_layers.size();
@ -1168,15 +1284,15 @@ void PrintObjectSupportMaterial::generate_base_layers(
assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z);
// Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON)
-- idx_top_contact_above;
idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
[&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z + EPSILON; });
// New polygons for layer_intermediate.
Polygons polygons_new;
// Use the precomputed layer_support_areas.
while (idx_object_layer_above > 0 && object.layers[idx_object_layer_above]->print_z > layer_intermediate.print_z - EPSILON)
-- idx_object_layer_above;
idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers, idx_object_layer_above,
[&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z - EPSILON; }));
polygons_new = layer_support_areas[idx_object_layer_above];
// Polygons to trim polygons_new.
@ -1212,9 +1328,8 @@ void PrintObjectSupportMaterial::generate_base_layers(
// 3) base.print_z > bottom.bottom_z && base.bottom_z < bottom.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the bottom layer height where it overlaps the base layer. No trimming needed here.
// 4) base.print_z > bottom.print_z && base.bottom_z >= bottom.print_z -> Base overlaps with bottom.print_z. This must not happen.
// 5) base.print_z <= bottom.print_z && base.bottom_z >= bottom.bottom_z -> Base is fully inside top. Trim base by top.
while (idx_bottom_contact_overlapping >= 0 &&
bottom_contacts[idx_bottom_contact_overlapping]->bottom_print_z() > layer_intermediate.print_z - EPSILON)
-- idx_bottom_contact_overlapping;
idx_bottom_contact_overlapping = idx_lower_or_equal(bottom_contacts, idx_bottom_contact_overlapping,
[&layer_intermediate](const MyLayer *layer){ return layer->bottom_print_z() <= layer_intermediate.print_z - EPSILON; });
// Collect all the bottom_contacts layer intersecting with this layer.
for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) {
MyLayer &layer_bottom_overlapping = *bottom_contacts[i];
@ -1249,8 +1364,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
true); // safety offset to merge the touching source polygons
layer_intermediate.layer_type = sltBase;
/*
if (0) {
#if 0
// Fillet the base polygons and trim them again with the top, interface and contact layers.
$base->{$i} = diff(
offset2(
@ -1263,8 +1377,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
$trim_polygons,
false); // don't apply the safety offset.
}
*/
#endif
}
});
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_base_layers() in parallel - end";
#ifdef SLIC3R_DEBUG
for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++it)
@ -1284,26 +1400,38 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const coordf_t gap_extra_below,
const coordf_t gap_xy) const
{
//FIXME This could be trivially parallelized.
const coord_t gap_xy_scaled = scale_(gap_xy);
size_t idx_object_layer_overlapping = 0;
// For all intermediate support layers:
for (MyLayersPtr::iterator it_layer = support_layers.begin(); it_layer != support_layers.end(); ++ it_layer) {
BOOST_LOG_TRIVIAL(trace) << "Support generator - trim_support_layers_by_object - trimmming layer " <<
(it_layer - support_layers.begin()) << " of " << support_layers.size();
const float gap_xy_scaled = float(scale_(gap_xy));
MyLayer &support_layer = *(*it_layer);
if (support_layer.polygons.empty() || support_layer.print_z < m_slicing_params.raft_contact_top_z + EPSILON)
// Empty support layer or a raft layer, nothing to trim.
continue;
// Collect non-empty layers to be processed in parallel.
// This is a good idea as pulling a thread from a thread pool for an empty task is expensive.
MyLayersPtr nonempty_layers;
nonempty_layers.reserve(support_layers.size());
for (size_t idx_layer = 0; idx_layer < support_layers.size(); ++ idx_layer) {
MyLayer *support_layer = support_layers[idx_layer];
if (! support_layer->polygons.empty() && support_layer->print_z >= m_slicing_params.raft_contact_top_z + EPSILON)
// Non-empty support layer and not a raft layer.
nonempty_layers.push_back(support_layer);
}
// For all intermediate support layers:
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - start";
tbb::parallel_for(
tbb::blocked_range<size_t>(0, nonempty_layers.size()),
[this, &object, &nonempty_layers, gap_extra_above, gap_extra_below, gap_xy_scaled](const tbb::blocked_range<size_t>& range) {
size_t idx_object_layer_overlapping = size_t(-1);
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
MyLayer &support_layer = *nonempty_layers[idx_layer];
// BOOST_LOG_TRIVIAL(trace) << "Support generator - trim_support_layers_by_object - trimmming non-empty layer " << idx_layer << " of " << nonempty_layers.size();
assert(! support_layer.polygons.empty() && support_layer.print_z >= m_slicing_params.raft_contact_top_z + EPSILON);
// Find the overlapping object layers including the extra above / below gap.
while (idx_object_layer_overlapping < object.layer_count() &&
object.get_layer(idx_object_layer_overlapping)->print_z < support_layer.print_z - support_layer.height - gap_extra_below + EPSILON)
++ idx_object_layer_overlapping;
coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON;
idx_object_layer_overlapping = idx_higher_or_equal(
object.layers, idx_object_layer_overlapping,
[z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; });
// Collect all the object layers intersecting with this layer.
Polygons polygons_trimming;
for (int i = idx_object_layer_overlapping; i < object.layer_count(); ++ i) {
const Layer &object_layer = *object.get_layer(i);
for (size_t i = idx_object_layer_overlapping; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
polygons_append(polygons_trimming, (Polygons)object_layer.slices);
@ -1317,10 +1445,11 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
support_layer.polygons,
offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
});
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
}
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raft_base(
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_layers,
@ -1411,7 +1540,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
// Convert some of the intermediate layers into top/bottom interface layers.
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_interface_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
@ -1430,20 +1558,23 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
MyLayersPtr interface_layers;
// Contact layer is considered an interface layer, therefore run the following block only if support_material_interface_layers > 1.
if (! intermediate_layers.empty() && m_object_config->support_material_interface_layers.value > 1) {
// Index of the first top contact layer intersecting the current intermediate layer.
size_t idx_top_contact_first = 0;
// Index of the first bottom contact layer intersecting the current intermediate layer.
size_t idx_bottom_contact_first = 0;
// For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
//FIXME this could be parallelized.
for (size_t idx_intermediate_layer = 0; idx_intermediate_layer < intermediate_layers.size(); ++ idx_intermediate_layer) {
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
interface_layers.assign(intermediate_layers.size(), nullptr);
tbb::spin_mutex layer_storage_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(0, intermediate_layers.size()),
[this, &bottom_contacts, &top_contacts, &intermediate_layers, &layer_storage, &layer_storage_mutex, &interface_layers](const tbb::blocked_range<size_t>& range) {
// Index of the first top contact layer intersecting the current intermediate layer.
size_t idx_top_contact_first = size_t(-1);
// Index of the first bottom contact layer intersecting the current intermediate layer.
size_t idx_bottom_contact_first = size_t(-1);
for (size_t idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) {
MyLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer];
// Top / bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces.
coordf_t top_z = intermediate_layers[std::min<int>(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z;
coordf_t bottom_z = intermediate_layers[std::max<int>(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z;
// Move idx_top_contact_first up until above the current print_z.
while (idx_top_contact_first < top_contacts.size() && top_contacts[idx_top_contact_first]->print_z < intermediate_layer.print_z)
++ idx_top_contact_first;
idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; });
// Collect the top contact areas above this intermediate layer, below top_z.
Polygons polygons_top_contact_projected;
for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) {
@ -1453,8 +1584,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
polygons_append(polygons_top_contact_projected, top_contact_layer.polygons);
}
// Move idx_bottom_contact_first up until touching bottom_z.
while (idx_bottom_contact_first < bottom_contacts.size() && bottom_contacts[idx_bottom_contact_first]->print_z + EPSILON < bottom_z)
++ idx_bottom_contact_first;
idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const MyLayer *layer){ return layer->print_z >= bottom_z - EPSILON; });
// Collect the top contact areas above this intermediate layer, below top_z.
Polygons polygons_bottom_contact_projected;
for (size_t idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < bottom_contacts.size(); ++ idx_bottom_contact) {
@ -1468,13 +1598,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
continue;
// Insert a new layer into top_interface_layers.
MyLayer &layer_new = layer_allocate(layer_storage,
MyLayer &layer_new = layer_allocate(layer_storage, layer_storage_mutex,
polygons_top_contact_projected.empty() ? sltBottomInterface : sltTopInterface);
layer_new.print_z = intermediate_layer.print_z;
layer_new.bottom_z = intermediate_layer.bottom_z;
layer_new.height = intermediate_layer.height;
layer_new.bridging = intermediate_layer.bridging;
interface_layers.push_back(&layer_new);
interface_layers[idx_intermediate_layer] = &layer_new;
polygons_append(polygons_top_contact_projected, polygons_bottom_contact_projected);
polygons_top_contact_projected = union_(polygons_top_contact_projected, true);
@ -1483,6 +1613,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
// $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
intermediate_layer.polygons = diff(intermediate_layer.polygons, polygons_top_contact_projected, false);
}
});
// Compress contact_out, remove the nullptr items.
remove_nulls(interface_layers);
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
}
return interface_layers;
@ -1620,7 +1755,7 @@ struct LoopInterfaceProcessor
// Generate loop contacts at the top_contact_layer,
// trim the top_contact_layer->polygons with the areas covered by the loops.
void generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src);
void generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const;
int n_contact_loops;
coordf_t circle_radius;
@ -1628,7 +1763,7 @@ struct LoopInterfaceProcessor
Polygon circle;
};
void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src)
void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const Flow &interface_flow_src) const
{
if (n_contact_loops == 0 || top_contact_layer.empty())
return;
@ -2135,14 +2270,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
infill_pattern = ipHoneycomb;
break;
}
std::unique_ptr<Fill> filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(ipRectilinear));
std::unique_ptr<Fill> filler_support = std::unique_ptr<Fill>(Fill::new_from_type(infill_pattern));
{
// BoundingBox bbox_object = object.bounding_box();
BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.)));
filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
}
// const coordf_t link_max_length_factor = 3.;
const coordf_t link_max_length_factor = 0.;
@ -2176,8 +2304,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
//FIXME Parallelize the support generator.
// Insert the raft base layers.
size_t support_layer_id = 0;
for (; support_layer_id < size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); ++ support_layer_id) {
size_t n_raft_layers = size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1));
tbb::parallel_for(tbb::blocked_range<size_t>(0, n_raft_layers),
[this, &object, &raft_layers, &loop_interface_processor,
infill_pattern, &bbox_object, support_density, interface_density, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor, with_sheath]
(const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{
assert(support_layer_id < raft_layers.size());
SupportLayer &support_layer = *object.support_layers[support_layer_id];
assert(support_layer.support_fills.entities.empty());
@ -2185,6 +2318,11 @@ void PrintObjectSupportMaterial::generate_toolpaths(
assert(support_layer.support_islands.expolygons.empty());
MyLayer &raft_layer = *raft_layers[support_layer_id];
std::unique_ptr<Fill> filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(ipRectilinear));
std::unique_ptr<Fill> filler_support = std::unique_ptr<Fill>(Fill::new_from_type(infill_pattern));
filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
// Print the support base below the support columns, or the support base for the support columns plus the contacts.
if (support_layer_id > 0) {
Polygons to_infill_polygons = (support_layer_id < m_slicing_params.base_raft_layers) ?
@ -2192,18 +2330,18 @@ void PrintObjectSupportMaterial::generate_toolpaths(
//FIXME misusing contact_polygons for support columns.
((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons);
if (! to_infill_polygons.empty()) {
Flow flow(m_support_material_flow.width, raft_layer.height, m_support_material_flow.nozzle_diameter, raft_layer.bridging);
Flow flow(float(m_support_material_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging);
// find centerline of the external loop/extrusions
ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ?
// union_ex(base_polygons, true) :
offset2_ex(to_infill_polygons, SCALED_EPSILON, - SCALED_EPSILON) :
offset2_ex(to_infill_polygons, SCALED_EPSILON, - SCALED_EPSILON - 0.5*flow.scaled_width());
offset2_ex(to_infill_polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON)) :
offset2_ex(to_infill_polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON - 0.5*flow.scaled_width()));
if (! to_infill.empty() && with_sheath) {
// Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove.
// TODO: use brim ordering algorithm
to_infill_polygons = to_polygons(to_infill);
// TODO: use offset2_ex()
to_infill = offset_ex(to_infill, - flow.scaled_spacing());
to_infill = offset_ex(to_infill, float(- flow.scaled_spacing()));
extrusion_entities_append_paths(
support_layer.support_fills.entities,
to_polylines(STDMOVE(to_infill_polygons)),
@ -2215,21 +2353,20 @@ void PrintObjectSupportMaterial::generate_toolpaths(
Fill *filler = filler_support.get();
filler->angle = raft_angle_base;
filler->spacing = m_support_material_flow.spacing();
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / support_density);
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density));
fill_expolygons_generate_paths(
// Destination
support_layer.support_fills.entities,
// Regions to fill
STDMOVE(to_infill),
// Filler and its parameters
filler, support_density,
filler, float(support_density),
// Extrusion parameters
erSupportMaterial, flow);
}
}
}
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
Fill *filler = filler_interface.get();
Flow flow = m_first_layer_flow;
float density = 0.f;
@ -2243,41 +2380,67 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// We don't use $base_flow->spacing because we need a constant spacing
// value that guarantees that all layers are correctly aligned.
filler->spacing = m_support_material_flow.spacing();
flow = Flow(m_support_material_interface_flow.width, raft_layer.height, m_support_material_flow.nozzle_diameter, raft_layer.bridging);
density = interface_density;
flow = Flow(float(m_support_material_interface_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging);
density = float(interface_density);
} else
continue;
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / density);
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
fill_expolygons_generate_paths(
// Destination
support_layer.support_fills.entities,
// Regions to fill
offset2_ex(raft_layer.polygons, SCALED_EPSILON, - SCALED_EPSILON),
offset2_ex(raft_layer.polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON)),
// Filler and its parameters
filler, density,
// Extrusion parameters
(support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow);
}
});
struct LayerCacheItem {
LayerCacheItem(MyLayerExtruded *layer_extruded = nullptr) : layer_extruded(layer_extruded) {}
MyLayerExtruded *layer_extruded;
std::vector<MyLayer*> overlapping;
};
struct LayerCache {
MyLayerExtruded bottom_contact_layer;
MyLayerExtruded top_contact_layer;
MyLayerExtruded base_layer;
MyLayerExtruded interface_layer;
std::vector<LayerCacheItem> overlaps;
};
std::vector<LayerCache> layer_caches(object.support_layers.size(), LayerCache());
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers.size()),
[this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor,
infill_pattern, &bbox_object, support_density, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath]
(const tbb::blocked_range<size_t>& range) {
// Indices of the 1st layer in their respective container at the support layer height.
size_t idx_layer_bottom_contact = 0;
size_t idx_layer_top_contact = 0;
size_t idx_layer_intermediate = 0;
size_t idx_layer_inteface = 0;
for (; support_layer_id < object.support_layers.size(); ++ support_layer_id)
std::unique_ptr<Fill> filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(ipRectilinear));
std::unique_ptr<Fill> filler_support = std::unique_ptr<Fill>(Fill::new_from_type(infill_pattern));
filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{
SupportLayer &support_layer = *object.support_layers[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
// Find polygons with the same print_z.
MyLayerExtruded bottom_contact_layer;
MyLayerExtruded top_contact_layer;
MyLayerExtruded base_layer;
MyLayerExtruded interface_layer;
MyLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer;
MyLayerExtruded &top_contact_layer = layer_cache.top_contact_layer;
MyLayerExtruded &base_layer = layer_cache.base_layer;
MyLayerExtruded &interface_layer = layer_cache.interface_layer;
// Increment the layer indices to find a layer at support_layer.print_z.
for (; idx_layer_bottom_contact < bottom_contacts .size() && bottom_contacts [idx_layer_bottom_contact]->print_z < support_layer.print_z - EPSILON; ++ idx_layer_bottom_contact) ;
for (; idx_layer_top_contact < top_contacts .size() && top_contacts [idx_layer_top_contact ]->print_z < support_layer.print_z - EPSILON; ++ idx_layer_top_contact ) ;
for (; idx_layer_intermediate < intermediate_layers.size() && intermediate_layers[idx_layer_intermediate ]->print_z < support_layer.print_z - EPSILON; ++ idx_layer_intermediate ) ;
for (; idx_layer_inteface < interface_layers .size() && interface_layers [idx_layer_inteface ]->print_z < support_layer.print_z - EPSILON; ++ idx_layer_inteface ) ;
{
auto fun = [&support_layer](const MyLayer *l){ return l->print_z >= support_layer.print_z - EPSILON; };
idx_layer_bottom_contact = idx_higher_or_equal(bottom_contacts, idx_layer_bottom_contact, fun);
idx_layer_top_contact = idx_higher_or_equal(top_contacts, idx_layer_top_contact, fun);
idx_layer_intermediate = idx_higher_or_equal(intermediate_layers, idx_layer_intermediate, fun);
idx_layer_inteface = idx_higher_or_equal(interface_layers, idx_layer_inteface, fun);
}
// Copy polygons from the layers.
if (idx_layer_bottom_contact < bottom_contacts.size() && bottom_contacts[idx_layer_bottom_contact]->print_z < support_layer.print_z + EPSILON)
bottom_contact_layer.layer = bottom_contacts[idx_layer_bottom_contact];
@ -2324,8 +2487,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0;
Flow interface_flow(
layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width),
layer_ex.layer->height,
float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)),
float(layer_ex.layer->height),
m_support_material_interface_flow.nozzle_diameter,
layer_ex.layer->bridging);
filler_interface->angle = interface_as_base ?
@ -2334,14 +2497,14 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Use interface angle for the interface layers.
interface_angle;
filler_interface->spacing = m_support_material_interface_flow.spacing();
filler_interface->link_max_length = scale_(filler_interface->spacing * link_max_length_factor / interface_density);
filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / interface_density));
fill_expolygons_generate_paths(
// Destination
layer_ex.extrusions,
// Regions to fill
union_ex(layer_ex.polygons_to_extrude(), true),
// Filler and its parameters
filler_interface.get(), interface_density,
filler_interface.get(), float(interface_density),
// Extrusion parameters
erSupportMaterialInterface, interface_flow);
}
@ -2353,32 +2516,32 @@ void PrintObjectSupportMaterial::generate_toolpaths(
filler->angle = angles[support_layer_id % angles.size()];
// We don't use $base_flow->spacing because we need a constant spacing
// value that guarantees that all layers are correctly aligned.
Flow flow(m_support_material_flow.width, base_layer.layer->height, m_support_material_flow.nozzle_diameter, base_layer.layer->bridging);
Flow flow(float(m_support_material_flow.width), float(base_layer.layer->height), m_support_material_flow.nozzle_diameter, base_layer.layer->bridging);
filler->spacing = m_support_material_flow.spacing();
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / support_density);
float density = support_density;
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density));
float density = float(support_density);
// find centerline of the external loop/extrusions
ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ?
// union_ex(base_polygons, true) :
offset2_ex(base_layer.polygons_to_extrude(), SCALED_EPSILON, - SCALED_EPSILON) :
offset2_ex(base_layer.polygons_to_extrude(), SCALED_EPSILON, - SCALED_EPSILON - 0.5*flow.scaled_width());
offset2_ex(base_layer.polygons_to_extrude(), float(SCALED_EPSILON), float(- SCALED_EPSILON)) :
offset2_ex(base_layer.polygons_to_extrude(), float(SCALED_EPSILON), float(- SCALED_EPSILON - 0.5*flow.scaled_width()));
if (base_layer.layer->bottom_z < EPSILON) {
// Base flange (the 1st layer).
filler = filler_interface.get();
filler->angle = Geometry::deg2rad(m_object_config->support_material_angle + 90.);
filler->angle = Geometry::deg2rad(float(m_object_config->support_material_angle) + 90.f);
density = 0.5f;
flow = m_first_layer_flow;
// use the proper spacing for first layer as we don't need to align
// its pattern to the other layers
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
filler->spacing = flow.spacing();
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / density);
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
} else if (with_sheath) {
// Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove.
// TODO: use brim ordering algorithm
Polygons to_infill_polygons = to_polygons(to_infill);
// TODO: use offset2_ex()
to_infill = offset_ex(to_infill, - flow.scaled_spacing());
to_infill = offset_ex(to_infill, - float(flow.scaled_spacing()));
extrusion_entities_append_paths(
base_layer.extrusions,
to_polylines(STDMOVE(to_infill_polygons)),
@ -2395,26 +2558,24 @@ void PrintObjectSupportMaterial::generate_toolpaths(
erSupportMaterial, flow);
}
MyLayerExtrudedPtrs mylayers;
mylayers.reserve(4);
layer_cache.overlaps.reserve(4);
if (! bottom_contact_layer.empty())
mylayers.push_back(&bottom_contact_layer);
layer_cache.overlaps.push_back(&bottom_contact_layer);
if (! top_contact_layer.empty())
mylayers.push_back(&top_contact_layer);
layer_cache.overlaps.push_back(&top_contact_layer);
if (! interface_layer.empty())
mylayers.push_back(&interface_layer);
layer_cache.overlaps.push_back(&interface_layer);
if (! base_layer.empty())
mylayers.push_back(&base_layer);
layer_cache.overlaps.push_back(&base_layer);
// Sort the layers with the same print_z coordinate by their heights, thickest first.
std::sort(mylayers.begin(), mylayers.end(), [](const MyLayerExtruded *p1, const MyLayerExtruded *p2) { return p1->layer->height > p2->layer->height; });
std::sort(layer_cache.overlaps.begin(), layer_cache.overlaps.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; });
// Collect the support areas with this print_z into islands, as there is no need
// for retraction over these islands.
Polygons polys;
// Collect the extrusions, sorted by the bottom extrusion height.
for (MyLayerExtrudedPtrs::iterator it = mylayers.begin(); it != mylayers.end(); ++ it) {
MyLayerExtruded &layer = **it;
for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) {
// Collect islands to polys.
layer.polygons_append(polys);
layer_cache_item.layer_extruded->polygons_append(polys);
// The print_z of the top contact surfaces and bottom_z of the bottom contact surfaces are "free"
// in a sense that they are not synchronized with other support layers. As the top and bottom contact surfaces
// are inflated to achieve a better anchoring, it may happen, that these surfaces will at least partially
@ -2424,23 +2585,20 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// where it overlaps with another support layer.
//FIXME When printing a briging path, what is an equivalent height of the squished extrudate of the same width?
// Collect overlapping top/bottom surfaces.
MyLayersPtr overlapping;
overlapping.reserve(16);
coordf_t bottom_z = layer.layer->bottom_print_z() + EPSILON;
layer_cache_item.overlapping.reserve(16);
coordf_t bottom_z = layer_cache_item.layer_extruded->layer->bottom_print_z() + EPSILON;
for (int i = int(idx_layer_bottom_contact) - 1; i >= 0 && bottom_contacts[i]->print_z > bottom_z; -- i)
overlapping.push_back(bottom_contacts[i]);
layer_cache_item.overlapping.push_back(bottom_contacts[i]);
for (int i = int(idx_layer_top_contact) - 1; i >= 0 && top_contacts[i]->print_z > bottom_z; -- i)
overlapping.push_back(top_contacts[i]);
if (layer.layer->layer_type == sltBottomContact) {
layer_cache_item.overlapping.push_back(top_contacts[i]);
if (layer_cache_item.layer_extruded->layer->layer_type == sltBottomContact) {
// Bottom contact layer may overlap with a base layer, which may be changed to interface layer.
for (int i = int(idx_layer_intermediate) - 1; i >= 0 && intermediate_layers[i]->print_z > bottom_z; -- i)
overlapping.push_back(intermediate_layers[i]);
layer_cache_item.overlapping.push_back(intermediate_layers[i]);
for (int i = int(idx_layer_inteface) - 1; i >= 0 && interface_layers[i]->print_z > bottom_z; -- i)
overlapping.push_back(interface_layers[i]);
layer_cache_item.overlapping.push_back(interface_layers[i]);
}
std::sort(overlapping.begin(), overlapping.end(), MyLayersPtrCompare());
modulate_extrusion_by_overlapping_layers(layer.extrusions, *layer.layer, overlapping);
support_layer.support_fills.append(std::move(layer.extrusions));
std::sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), MyLayersPtrCompare());
}
if (! polys.empty())
expolygons_append(support_layer.support_islands.expolygons, union_ex(polys));
@ -2454,6 +2612,21 @@ void PrintObjectSupportMaterial::generate_toolpaths(
);
} */
} // for each support_layer_id
});
// Now modulate the support layer height in parallel.
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers.size()),
[this, &object, &layer_caches]
(const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) {
SupportLayer &support_layer = *object.support_layers[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) {
modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping);
support_layer.support_fills.append(std::move(layer_cache_item.layer_extruded->extrusions));
}
}
});
}
/*

View File

@ -162,8 +162,7 @@ private:
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayerStorage &layer_storage,
const coordf_t max_object_layer_height) const;
MyLayerStorage &layer_storage) const;
// Fill in the base layers with polygons.
void generate_base_layers(
@ -171,12 +170,11 @@ private:
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,
std::vector<Polygons> &layer_support_areas) const;
const std::vector<Polygons> &layer_support_areas) const;
// Generate raft layers, also expand the 1st support layer
// in case there is no raft layer to improve support adhesion.
MyLayersPtr generate_raft_base(
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_layers,
@ -184,7 +182,6 @@ private:
// Turn some of the base layers into interface layers.
MyLayersPtr generate_interface_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts,
MyLayersPtr &intermediate_layers,