Improvements of the new support generator:

Variable path thickness for all support layers to avoid over-extrusion.
Supports only in grid cells below the top contacts.
Provision for filtering excessively long perimeter pieces of the support infill lines.
This commit is contained in:
bubnikv 2017-01-20 15:21:05 +01:00
parent 08351b5e48
commit ff25c0ccc2
2 changed files with 144 additions and 62 deletions

View File

@ -102,6 +102,35 @@ void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial
export_support_surface_type_legend_to_svg(svg, legend_pos); export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close(); svg.Close();
} }
void export_print_z_polygons_and_extrusions_to_svg(
const char *path,
PrintObjectSupportMaterial::MyLayer ** const layers,
size_t n_layers,
SupportLayer &support_layer)
{
BoundingBox bbox;
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
Point legend_pos(bbox.min.x, bbox.max.y);
bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
svg.draw(union_ex(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type), transparency);
for (int i = 0; i < n_layers; ++ i)
svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
Polygons polygons_support, polygons_interface;
support_layer.support_fills.polygons_covered_by_width(polygons_support, SCALED_EPSILON);
support_layer.support_interface_fills.polygons_covered_by_width(polygons_interface, SCALED_EPSILON);
svg.draw(union_ex(polygons_support), "brown");
svg.draw(union_ex(polygons_interface), "black");
export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close();
}
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) : PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object, const SlicingParameters &slicing_params) :
@ -344,9 +373,6 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
sl1->upper_layer = sl2; sl1->upper_layer = sl2;
sl2->lower_layer = sl1; sl2->lower_layer = sl1;
} }
#ifdef SLIC3R_DEBUG
export_print_z_polygons_to_svg(debug_out_path("support-%d-%lf.svg", iRun, zavg).c_str(), layers_sorted.data() + i, j - i);
#endif
i = j; i = j;
++ layer_id; ++ layer_id;
} }
@ -356,6 +382,28 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// Generate the actual toolpaths and save them into each layer. // Generate the actual toolpaths and save them into each layer.
this->generate_toolpaths(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers); this->generate_toolpaths(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers);
#ifdef SLIC3R_DEBUG
{
size_t layer_id = 0;
for (int i = 0; i < int(layers_sorted.size());) {
// Find the last layer with roughly the same print_z, find the minimum layer height of all.
// Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should.
int j = i + 1;
coordf_t zmax = layers_sorted[i]->print_z + EPSILON;
for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ;
export_print_z_polygons_to_svg(
debug_out_path("support-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(),
layers_sorted.data() + i, j - i);
export_print_z_polygons_and_extrusions_to_svg(
debug_out_path("support-w-fills-%d-%lf.svg", iRun, layers_sorted[i]->print_z).c_str(),
layers_sorted.data() + i, j - i,
*object.support_layers[layer_id]);
i = j;
++ layer_id;
}
}
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - End"; BOOST_LOG_TRIVIAL(info) << "Support generator - End";
} }
@ -697,12 +745,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
if (new_layer.print_z < this->first_layer_height() + m_support_layer_height_min) if (new_layer.print_z < this->first_layer_height() + m_support_layer_height_min)
continue; continue;
#if 1 #if 0
new_layer.polygons = std::move(contact_polygons);
#else
{ {
// Create an EdgeGrid, initialize it with projection, initialize signed distance field. // Create an EdgeGrid, initialize it with projection, initialize signed distance field.
Slic3r::EdgeGrid::Grid grid; Slic3r::EdgeGrid::Grid grid;
coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing(); 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)); // scale_(1.5f);
BoundingBox bbox = get_extents(contact_polygons); BoundingBox bbox = get_extents(contact_polygons);
bbox.offset(20); bbox.offset(20);
bbox.align_to_grid(grid_resolution); bbox.align_to_grid(grid_resolution);
@ -710,18 +760,23 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
grid.create(contact_polygons, grid_resolution); grid.create(contact_polygons, grid_resolution);
grid.calculate_sdf(); grid.calculate_sdf();
// Extract a bounding contour from the grid, trim by the object. // Extract a bounding contour from the grid, trim by the object.
contact_polygons = diff( // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
new_layer.polygons = diff(
grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 5), grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 5),
slices_margin_cached, slices_margin_cached,
true); true);
// 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells.
new_layer.contact_polygons = new Polygons(diff(
grid.contours_simplified(-3),
slices_margin_cached,
false));
} }
#endif #endif
new_layer.polygons = std::move(contact_polygons); // Store the overhang polygons.
// Store the overhang polygons as the aux_polygons.
// The overhang polygons are used in the path generator for planning of the contact loops. // The overhang polygons are used in the path generator for planning of the contact loops.
// if (this->has_contact_loops()) // if (this->has_contact_loops())
new_layer.aux_polygons = new Polygons(std::move(overhang_polygons)); new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
contact_out.push_back(&new_layer); contact_out.push_back(&new_layer);
if (0) { if (0) {
@ -775,10 +830,16 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
Polygons polygons_new; Polygons polygons_new;
// Contact surfaces are expanded away from the object, trimmed by the object. // Contact surfaces are expanded away from the object, trimmed by the object.
// Use a slight positive offset to overlap the touching regions. // Use a slight positive offset to overlap the touching regions.
polygons_append(polygons_new, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); #if 0
// Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON));
#else
// Consume the contact_polygons. The contact polygons are already expanded into a grid form.
polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons));
#endif
// These are the overhang surfaces. They are touching the object and they are not expanded away from the object. // These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
// Use a slight positive offset to overlap the touching regions. // Use a slight positive offset to overlap the touching regions.
polygons_append(polygons_new, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); polygons_append(polygons_new, offset(*top_contacts[contact_idx]->overhang_polygons, SCALED_EPSILON));
polygons_append(projection, union_(polygons_new)); polygons_append(projection, union_(polygons_new));
} }
if (projection.empty()) if (projection.empty())
@ -864,7 +925,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// Cache the slice of a support volume. The support volume is expanded by 1/2 of support material flow spacing // 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. // to allow a placement of suppot zig-zag snake along the grid lines.
layer_support_areas[layer_id] = diff( layer_support_areas[layer_id] = diff(
grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 5), grid.contours_simplified(m_support_material_flow.scaled_spacing()/2 + 25),
to_polygons(layer.slices.expolygons), to_polygons(layer.slices.expolygons),
true); true);
@ -1417,7 +1478,7 @@ struct MyLayerExtruded
bool could_merge(const MyLayerExtruded &other) const { bool could_merge(const MyLayerExtruded &other) const {
return ! this->empty() && ! other.empty() && return ! this->empty() && ! other.empty() &&
this->layer->height == other.layer->height && std::abs(this->layer->height - other.layer->height) < EPSILON &&
this->layer->bridging == other.layer->bridging; this->layer->bridging == other.layer->bridging;
} }
@ -1477,7 +1538,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
Polygons overhang_polygons; Polygons overhang_polygons;
// if (top_contact_layer.layer->aux_polygons != nullptr) // if (top_contact_layer.layer->aux_polygons != nullptr)
overhang_polygons = std::move(*top_contact_layer.layer->aux_polygons); overhang_polygons = std::move(*top_contact_layer.layer->overhang_polygons);
// Generate the outermost loop. // Generate the outermost loop.
// Find centerline of the external loop (or any other kind of extrusions should the loop be skipped) // Find centerline of the external loop (or any other kind of extrusions should the loop be skipped)
@ -1562,10 +1623,10 @@ void modulate_extrusion_by_overlapping_layers(
ExtrusionEntitiesPtr &extrusions_in_out, ExtrusionEntitiesPtr &extrusions_in_out,
const PrintObjectSupportMaterial::MyLayer &this_layer, const PrintObjectSupportMaterial::MyLayer &this_layer,
// Multiple layers overlapping with this_layer, sorted bottom up. // Multiple layers overlapping with this_layer, sorted bottom up.
const PrintObjectSupportMaterial::MyLayer * const *overlapping_layers, const PrintObjectSupportMaterial::MyLayersPtr &overlapping_layers)
const size_t n_overlaping_layers)
{ {
if (n_overlaping_layers == 0 || extrusions_in_out.empty()) size_t n_overlapping_layers = overlapping_layers.size();
if (n_overlapping_layers == 0 || extrusions_in_out.empty())
// The extrusions do not overlap with any other extrusion. // The extrusions do not overlap with any other extrusion.
return; return;
@ -1589,7 +1650,7 @@ void modulate_extrusion_by_overlapping_layers(
// Split the extrusions by the overlapping layers, reduce their extrusion rate. // Split the extrusions by the overlapping layers, reduce their extrusion rate.
// The last path_fragment is from this_layer. // The last path_fragment is from this_layer.
std::vector<ExtrusionPathFragment> path_fragments( std::vector<ExtrusionPathFragment> path_fragments(
n_overlaping_layers + 1, n_overlapping_layers + 1,
ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height)); ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height));
// Don't use it, it will be released. // Don't use it, it will be released.
extrusion_path_template = nullptr; extrusion_path_template = nullptr;
@ -1598,7 +1659,7 @@ void modulate_extrusion_by_overlapping_layers(
static int iRun = 0; static int iRun = 0;
++ iRun; ++ iRun;
BoundingBox bbox; BoundingBox bbox;
for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlaping_layers; ++ i_overlapping_layer) { for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) {
const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer];
bbox.merge(get_extents(overlapping_layer.polygons)); bbox.merge(get_extents(overlapping_layer.polygons));
} }
@ -1611,13 +1672,13 @@ void modulate_extrusion_by_overlapping_layers(
const float transparency = 0.5f; const float transparency = 0.5f;
// Filled polygons for the overlapping regions. // Filled polygons for the overlapping regions.
svg.draw(union_ex(this_layer.polygons), dbg_index_to_color(-1), transparency); svg.draw(union_ex(this_layer.polygons), dbg_index_to_color(-1), transparency);
for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlaping_layers; ++ i_overlapping_layer) { for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) {
const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer];
svg.draw(union_ex(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), transparency); svg.draw(union_ex(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), transparency);
} }
// Contours of the overlapping regions. // Contours of the overlapping regions.
svg.draw(to_polylines(this_layer.polygons), dbg_index_to_color(-1), scale_(0.2)); svg.draw(to_polylines(this_layer.polygons), dbg_index_to_color(-1), scale_(0.2));
for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlaping_layers; ++ i_overlapping_layer) { for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlapping_layers; ++ i_overlapping_layer) {
const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer];
svg.draw(to_polylines(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), scale_(0.1)); svg.draw(to_polylines(overlapping_layer.polygons), dbg_index_to_color(int(i_overlapping_layer)), scale_(0.1));
} }
@ -1646,7 +1707,7 @@ void modulate_extrusion_by_overlapping_layers(
// Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z. // Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z.
// Trim by the highest overlapping layer first. // Trim by the highest overlapping layer first.
for (int i_overlapping_layer = int(n_overlaping_layers) - 1; i_overlapping_layer >= 0; -- i_overlapping_layer) { for (int i_overlapping_layer = int(n_overlapping_layers) - 1; i_overlapping_layer >= 0; -- i_overlapping_layer) {
const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer]; const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer];
ExtrusionPathFragment &frag = path_fragments[i_overlapping_layer]; ExtrusionPathFragment &frag = path_fragments[i_overlapping_layer];
Polygons polygons_trimming = offset(union_ex(overlapping_layer.polygons), scale_(0.5*extrusion_width)); Polygons polygons_trimming = offset(union_ex(overlapping_layer.polygons), scale_(0.5*extrusion_width));
@ -1683,7 +1744,7 @@ void modulate_extrusion_by_overlapping_layers(
bool is_start; bool is_start;
}; };
std::unordered_multimap<Point, ExtrusionPathFragmentEnd, PointHash> map_fragment_starts; std::unordered_multimap<Point, ExtrusionPathFragmentEnd, PointHash> map_fragment_starts;
for (size_t i_overlapping_layer = 0; i_overlapping_layer <= n_overlaping_layers; ++ i_overlapping_layer) { for (size_t i_overlapping_layer = 0; i_overlapping_layer <= n_overlapping_layers; ++ i_overlapping_layer) {
const Polylines &polylines = path_fragments[i_overlapping_layer].polylines; const Polylines &polylines = path_fragments[i_overlapping_layer].polylines;
for (size_t i_polyline = 0; i_polyline < polylines.size(); ++ i_polyline) { for (size_t i_polyline = 0; i_polyline < polylines.size(); ++ i_polyline) {
// Map a starting point of a polyline to a pair of <layer, polyline> // Map a starting point of a polyline to a pair of <layer, polyline>
@ -1706,7 +1767,7 @@ void modulate_extrusion_by_overlapping_layers(
const Point &pt_end = path_ends[i_path].second; const Point &pt_end = path_ends[i_path].second;
Point pt_current = pt_start; Point pt_current = pt_start;
// Find a chain of fragments with the original / reduced print height. // Find a chain of fragments with the original / reduced print height.
ExtrusionEntityCollection eec; ExtrusionMultiPath multipath;
for (;;) { for (;;) {
// Iterate over 4 closest grid cells around pt_current, // Iterate over 4 closest grid cells around pt_current,
// find the closest start point inside these cells to pt_current. // find the closest start point inside these cells to pt_current.
@ -1747,7 +1808,7 @@ void modulate_extrusion_by_overlapping_layers(
ExtrusionPathFragment &frag = path_fragments[fragment_end_min.layer_idx]; ExtrusionPathFragment &frag = path_fragments[fragment_end_min.layer_idx];
Polyline &frag_polyline = frag.polylines[fragment_end_min.polyline_idx]; Polyline &frag_polyline = frag.polylines[fragment_end_min.polyline_idx];
// Path to append the fragment to. // Path to append the fragment to.
ExtrusionPath *path = eec.entities.empty() ? nullptr : dynamic_cast<ExtrusionPath*>(eec.entities.back()); ExtrusionPath *path = multipath.paths.empty() ? nullptr : &multipath.paths.back();
if (path != nullptr) { if (path != nullptr) {
// Verify whether the path is compatible with the current fragment. It shall not be if the path was not split errorneously by the Clipper library. // Verify whether the path is compatible with the current fragment. It shall not be if the path was not split errorneously by the Clipper library.
assert(path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm); assert(path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm);
@ -1756,8 +1817,8 @@ void modulate_extrusion_by_overlapping_layers(
} }
if (path == nullptr) { if (path == nullptr) {
// Allocate a new path. // Allocate a new path.
path = new ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height); multipath.paths.push_back(ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height));
eec.entities.push_back(path); path = &multipath.paths.back();
} }
// The Clipper library may flip the order of the clipped polylines arbitrarily. // The Clipper library may flip the order of the clipped polylines arbitrarily.
// Reverse the source polyline, if connecting to the end. // Reverse the source polyline, if connecting to the end.
@ -1777,15 +1838,14 @@ void modulate_extrusion_by_overlapping_layers(
break; break;
} }
} }
if (! eec.empty()) { if (!multipath.paths.empty()) {
if (eec.entities.size() == 1) { if (multipath.paths.size() == 1) {
// This path was not fragmented. // This path was not fragmented.
extrusions_in_out.push_back(eec.entities.front()); extrusions_in_out.push_back(new ExtrusionPath(std::move(multipath.paths.front())));
eec.entities.pop_back();
} else { } else {
// This path was fragmented. Copy the collection as a whole object, so the order inside the collection will not be changed // This path was fragmented. Copy the collection as a whole object, so the order inside the collection will not be changed
// during the chaining of extrusions_in_out. // during the chaining of extrusions_in_out.
extrusions_in_out.push_back(new ExtrusionEntityCollection(std::move(eec))); extrusions_in_out.push_back(new ExtrusionMultiPath(std::move(multipath)));
} }
} }
} }
@ -1841,6 +1901,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
interface_density = support_density; interface_density = support_density;
} }
// const coordf_t link_max_length_factor = 3.;
const coordf_t link_max_length_factor = 0.;
//FIXME Parallelize the support generator: //FIXME Parallelize the support generator:
/* /*
Slic3r::parallelize( Slic3r::parallelize(
@ -1873,6 +1936,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// value that guarantees that all layers are correctly aligned. // value that guarantees that all layers are correctly aligned.
Flow flow(m_support_material_flow.width, raft_layer.height, m_support_material_flow.nozzle_diameter, raft_layer.bridging); Flow flow(m_support_material_flow.width, raft_layer.height, m_support_material_flow.nozzle_diameter, raft_layer.bridging);
filler->spacing = m_support_material_flow.spacing(); filler->spacing = m_support_material_flow.spacing();
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / support_density);
float density = support_density; float density = support_density;
// find centerline of the external loop/extrusions // find centerline of the external loop/extrusions
ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ?
@ -1889,6 +1953,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// its pattern to the other layers // its pattern to the other layers
//FIXME When paralellizing, each thread shall have its own copy of the fillers. //FIXME When paralellizing, each thread shall have its own copy of the fillers.
filler->spacing = flow.spacing(); filler->spacing = flow.spacing();
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / density);
} else if (with_sheath) { } else if (with_sheath) {
// Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove.
// TODO: use brim ordering algorithm // TODO: use brim ordering algorithm
@ -1999,9 +2064,10 @@ void PrintObjectSupportMaterial::generate_toolpaths(
top_contact_layer.layer->bridging); top_contact_layer.layer->bridging);
filler_interface->angle = interface_angle; filler_interface->angle = interface_angle;
filler_interface->spacing = m_support_material_interface_flow.spacing(); filler_interface->spacing = m_support_material_interface_flow.spacing();
filler_interface->link_max_length = scale_(filler_interface->spacing * link_max_length_factor / interface_density);
fill_expolygons_generate_paths( fill_expolygons_generate_paths(
// Destination // Destination
support_layer.support_fills.entities, top_contact_layer.extrusions,
// Regions to fill // Regions to fill
union_ex(top_contact_layer.layer->polygons, true), union_ex(top_contact_layer.layer->polygons, true),
// Filler and its parameters // Filler and its parameters
@ -2020,9 +2086,10 @@ void PrintObjectSupportMaterial::generate_toolpaths(
interface_layer.layer->bridging); interface_layer.layer->bridging);
filler_interface->angle = interface_angle; filler_interface->angle = interface_angle;
filler_interface->spacing = m_support_material_interface_flow.spacing(); filler_interface->spacing = m_support_material_interface_flow.spacing();
filler_interface->link_max_length = scale_(filler_interface->spacing * link_max_length_factor / interface_density);
fill_expolygons_generate_paths( fill_expolygons_generate_paths(
// Destination // Destination
support_layer.support_fills.entities, interface_layer.extrusions,
// Regions to fill // Regions to fill
union_ex(interface_layer.layer->polygons, true), union_ex(interface_layer.layer->polygons, true),
// Filler and its parameters // Filler and its parameters
@ -2040,6 +2107,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// value that guarantees that all layers are correctly aligned. // 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(m_support_material_flow.width, base_layer.layer->height, m_support_material_flow.nozzle_diameter, base_layer.layer->bridging);
filler->spacing = m_support_material_flow.spacing(); filler->spacing = m_support_material_flow.spacing();
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / support_density);
float density = support_density; float density = support_density;
// find centerline of the external loop/extrusions // find centerline of the external loop/extrusions
ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ?
@ -2064,6 +2132,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// its pattern to the other layers // its pattern to the other layers
//FIXME When paralellizing, each thread shall have its own copy of the fillers. //FIXME When paralellizing, each thread shall have its own copy of the fillers.
filler->spacing = flow.spacing(); filler->spacing = flow.spacing();
filler->link_max_length = scale_(filler->spacing * link_max_length_factor / density);
} else if (with_sheath) { } else if (with_sheath) {
// Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove.
// TODO: use brim ordering algorithm // TODO: use brim ordering algorithm
@ -2071,13 +2140,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// TODO: use offset2_ex() // TODO: use offset2_ex()
to_infill = offset_ex(to_infill, - flow.scaled_spacing()); to_infill = offset_ex(to_infill, - flow.scaled_spacing());
extrusion_entities_append_paths( extrusion_entities_append_paths(
support_layer.support_fills.entities, base_layer.extrusions,
to_polylines(STDMOVE(to_infill_polygons)), to_polylines(STDMOVE(to_infill_polygons)),
erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height);
} }
fill_expolygons_generate_paths( fill_expolygons_generate_paths(
// Destination // Destination
support_layer.support_fills.entities, base_layer.extrusions,
// Regions to fill // Regions to fill
STDMOVE(to_infill), STDMOVE(to_infill),
// Filler and its parameters // Filler and its parameters
@ -2100,6 +2169,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Use interface angle for the interface layers. // Use interface angle for the interface layers.
interface_angle; interface_angle;
filler_interface->spacing = m_support_material_interface_flow.spacing(); filler_interface->spacing = m_support_material_interface_flow.spacing();
filler_interface->link_max_length = scale_(filler_interface->spacing * link_max_length_factor / interface_density);
fill_expolygons_generate_paths( fill_expolygons_generate_paths(
// Destination // Destination
bottom_contact_layer.extrusions, bottom_contact_layer.extrusions,
@ -2109,22 +2179,6 @@ void PrintObjectSupportMaterial::generate_toolpaths(
filler_interface.get(), interface_density, filler_interface.get(), interface_density,
// Extrusion parameters // Extrusion parameters
erSupportMaterial, interface_flow); erSupportMaterial, interface_flow);
// The bottom contact layer has been inflated to anchor the support better. It may be possible, that there is a bottom
// contact layer below this bottom contact layer overlapping with this one, leading to over-extrusion.
// Mitigate the over-extrusion by modulating the extrusion rate over these regions.
assert(bottom_contact_layer.layer->bridging);
//FIXME When printing a briging path, what is an equivalent height of the squished extrudate of the same width?
int idx_bottom_contact_non_overlapping = int(idx_layer_bottom_contact) - 1;
for (; idx_bottom_contact_non_overlapping >= 0; -- idx_bottom_contact_non_overlapping)
if (bottom_contacts[idx_bottom_contact_non_overlapping]->print_z <
bottom_contact_layer.layer->print_z - bottom_contact_layer.layer->height + EPSILON)
break;
++ idx_bottom_contact_non_overlapping;
modulate_extrusion_by_overlapping_layers(
bottom_contact_layer.extrusions,
*bottom_contact_layer.layer,
bottom_contacts.data() + idx_bottom_contact_non_overlapping,
idx_layer_bottom_contact - idx_bottom_contact_non_overlapping);
} }
// Collect the support areas with this print_z into islands, as there is no need // Collect the support areas with this print_z into islands, as there is no need
@ -2132,9 +2186,33 @@ void PrintObjectSupportMaterial::generate_toolpaths(
Polygons polys; Polygons polys;
// Collect the extrusions, sorted by the bottom extrusion height. // Collect the extrusions, sorted by the bottom extrusion height.
for (MyLayerExtrudedPtrs::iterator it = mylayers.begin(); it != mylayers.end(); ++ it) { for (MyLayerExtrudedPtrs::iterator it = mylayers.begin(); it != mylayers.end(); ++ it) {
(*it)->polygons_append(polys); MyLayerExtruded &layer = **it;
std::move(std::begin((*it)->extrusions), std::end((*it)->extrusions), // Collect islands to polys.
std::back_inserter(support_layer.support_fills.entities)); layer.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
// overlap in Z with another support layers, leading to over-extrusion.
// Mitigate the over-extrusion by modulating the extrusion rate over these regions.
// The print head will follow the same print_z, but the layer thickness will be reduced
// 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);
for (int i = int(idx_layer_bottom_contact) - 1; i >= 0; -- i) {
if (bottom_contacts[i]->print_z < layer.layer->print_z - layer.layer->height + EPSILON)
break;
overlapping.push_back(bottom_contacts[i]);
}
for (int i = int(idx_layer_top_contact) - 1; i >= 0; -- i) {
if (top_contacts[i]->print_z < layer.layer->print_z - layer.layer->height + EPSILON)
break;
overlapping.push_back(top_contacts[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));
} }
if (! polys.empty()) if (! polys.empty())
expolygons_append(support_layer.support_islands.expolygons, union_ex(polys)); expolygons_append(support_layer.support_islands.expolygons, union_ex(polys));

View File

@ -59,13 +59,16 @@ public:
idx_object_layer_above(size_t(-1)), idx_object_layer_above(size_t(-1)),
idx_object_layer_below(size_t(-1)), idx_object_layer_below(size_t(-1)),
bridging(false), bridging(false),
aux_polygons(NULL) contact_polygons(nullptr),
overhang_polygons(nullptr)
{} {}
~MyLayer() ~MyLayer()
{ {
delete aux_polygons; delete contact_polygons;
aux_polygons = NULL; contact_polygons = nullptr;
delete overhang_polygons;
overhang_polygons = nullptr;
} }
bool operator==(const MyLayer &layer2) const { bool operator==(const MyLayer &layer2) const {
@ -106,9 +109,10 @@ public:
// Polygons to be filled by the support pattern. // Polygons to be filled by the support pattern.
Polygons polygons; Polygons polygons;
// Currently for the contact layers only: Overhangs are stored here. // Currently for the contact layers only.
// MyLayer owns the aux_polygons, they are freed by the destructor. // MyLayer owns the contact_polygons and overhang_polygons, they are freed by the destructor.
Polygons *aux_polygons; Polygons *contact_polygons;
Polygons *overhang_polygons;
}; };
// Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained // Layers are allocated and owned by a deque. Once a layer is allocated, it is maintained