Fix of an inconsistent bottom contact layer thickness

in case two and more bottom contact layers overlap after their extension.

New method modulate_extrusion_by_overlapping_layers() reduces thickness
of an extrusion path where it overlaps in Z with some other paths.

The same trick has yet to be applied to the layers overlapping in Z with
top contact surfaces.
This commit is contained in:
bubnikv 2017-01-19 13:47:06 +01:00
parent 0b90ebd74e
commit d5f9db76b3

View File

@ -10,7 +10,7 @@
#include <cmath> #include <cmath>
#include <memory> #include <memory>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <unordered_set> #include <unordered_map>
// #define SLIC3R_DEBUG // #define SLIC3R_DEBUG
@ -98,7 +98,7 @@ void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial
for (int i = 0; i < n_layers; ++ i) 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); 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) for (int i = 0; i < n_layers; ++ i)
svg.draw(to_lines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type)); svg.draw(to_polylines(layers[i]->polygons), support_surface_type_to_color_name(layers[i]->layer_type));
export_support_surface_type_legend_to_svg(svg, legend_pos); export_support_surface_type_legend_to_svg(svg, legend_pos);
svg.Close(); svg.Close();
} }
@ -302,6 +302,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating layers"; BOOST_LOG_TRIVIAL(info) << "Support generator - Creating layers";
// For debugging purposes, one may want to show only some of the support extrusions.
// raft_layers.clear(); // raft_layers.clear();
// bottom_contacts.clear(); // bottom_contacts.clear();
// top_contacts.clear(); // top_contacts.clear();
@ -1126,9 +1127,9 @@ void PrintObjectSupportMaterial::generate_base_layers(
bbox.merge(get_extents(polygons_trimming)); bbox.merge(get_extents(polygons_trimming));
::Slic3r::SVG svg(debug_out_path("support-intermediate-layers-raw-%d-%lf.svg", iRun, layer_intermediate.print_z), bbox); ::Slic3r::SVG svg(debug_out_path("support-intermediate-layers-raw-%d-%lf.svg", iRun, layer_intermediate.print_z), bbox);
svg.draw(union_ex(polygons_new, false), "blue", 0.5f); svg.draw(union_ex(polygons_new, false), "blue", 0.5f);
svg.draw(to_lines(polygons_new), "blue"); svg.draw(to_polylines(polygons_new), "blue");
svg.draw(union_ex(polygons_trimming, true), "red", 0.5f); svg.draw(union_ex(polygons_trimming, true), "red", 0.5f);
svg.draw(to_lines(polygons_trimming), "red"); svg.draw(to_polylines(polygons_trimming), "red");
} }
#endif /* SLIC3R_DEBUG */ #endif /* SLIC3R_DEBUG */
@ -1536,6 +1537,260 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height); erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height);
} }
#ifdef SLIC3R_DEBUG
static std::string dbg_index_to_color(int idx)
{
if (idx < 0)
return "yellow";
idx = idx % 3;
switch (idx) {
case 0: return "red";
case 1: return "green";
default: return "blue";
}
}
#endif /* SLIC3R_DEBUG */
// When extruding a bottom interface layer over an object, the bottom interface layer is extruded in a thin air, therefore
// it is being extruded with a bridging flow to not shrink excessively (the die swell effect).
// Tiny extrusions are better avoided and it is always better to anchor the thread to an existing support structure if possible.
// Therefore the bottom interface spots are expanded a bit. The expanded regions may overlap with another bottom interface layers,
// leading to over extrusion, where they overlap. The over extrusion is better avoided as it often makes the interface layers
// to stick too firmly to the object.
void modulate_extrusion_by_overlapping_layers(
// Extrusions generated for this_layer.
ExtrusionEntitiesPtr &extrusions_in_out,
const PrintObjectSupportMaterial::MyLayer &this_layer,
// Multiple layers overlapping with this_layer, sorted bottom up.
const PrintObjectSupportMaterial::MyLayer * const *overlapping_layers,
const size_t n_overlaping_layers)
{
if (n_overlaping_layers == 0 || extrusions_in_out.empty())
// The extrusions do not overlap with any other extrusion.
return;
// Get the initial extrusion parameters.
ExtrusionPath *extrusion_path_template = dynamic_cast<ExtrusionPath*>(extrusions_in_out.front());
assert(extrusion_path_template != nullptr);
ExtrusionRole extrusion_role = extrusion_path_template->role;
float extrusion_width = extrusion_path_template->width;
struct ExtrusionPathFragment
{
ExtrusionPathFragment() : mm3_per_mm(-1), width(-1), height(-1) {};
ExtrusionPathFragment(double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height) {};
Polylines polylines;
double mm3_per_mm;
float width;
float height;
};
// Split the extrusions by the overlapping layers, reduce their extrusion rate.
// The last path_fragment is from this_layer.
std::vector<ExtrusionPathFragment> path_fragments(
n_overlaping_layers + 1,
ExtrusionPathFragment(extrusion_path_template->mm3_per_mm, extrusion_path_template->width, extrusion_path_template->height));
// Don't use it, it will be released.
extrusion_path_template = nullptr;
#ifdef SLIC3R_DEBUG
static int iRun = 0;
++ iRun;
BoundingBox bbox;
for (size_t i_overlapping_layer = 0; i_overlapping_layer < n_overlaping_layers; ++ i_overlapping_layer) {
const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer];
bbox.merge(get_extents(overlapping_layer.polygons));
}
for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) {
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(*it);
assert(path != nullptr);
bbox.merge(get_extents(path->polyline));
}
SVG svg(debug_out_path("support-fragments-%d-%lf.svg", iRun, this_layer.print_z).c_str(), bbox);
const float transparency = 0.5f;
// Filled polygons for the overlapping regions.
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) {
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);
}
// Contours of the overlapping regions.
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) {
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));
}
// Fill extrusion, the source.
for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) {
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(*it);
svg.draw(path->polyline, "magenta", scale_(0.2));
}
#endif /* SLIC3R_DEBUG */
// End points of the original paths.
std::vector<std::pair<Point, Point>> path_ends;
// Collect the paths of this_layer.
{
Polylines &polylines = path_fragments.back().polylines;
for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) {
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(*it);
assert(path != nullptr);
polylines.emplace_back(Polyline(std::move(path->polyline)));
path_ends.emplace_back(std::pair<Point, Point>(polylines.back().points.front(), polylines.back().points.back()));
}
}
// Destroy the original extrusion paths, their polylines were moved to path_fragments already.
// This will be the destination for the new paths.
extrusions_in_out.clear();
// Fragment the path segments by overlapping layers. The overlapping layers are sorted by an increasing print_z.
// Trim by the highest overlapping layer first.
for (int i_overlapping_layer = int(n_overlaping_layers) - 1; i_overlapping_layer >= 0; -- i_overlapping_layer) {
const PrintObjectSupportMaterial::MyLayer &overlapping_layer = *overlapping_layers[i_overlapping_layer];
ExtrusionPathFragment &frag = path_fragments[i_overlapping_layer];
Polygons polygons_trimming = offset(union_ex(overlapping_layer.polygons), scale_(0.5*extrusion_width));
frag.polylines = intersection_pl(path_fragments.back().polylines, polygons_trimming, false);
path_fragments.back().polylines = diff_pl(path_fragments.back().polylines, polygons_trimming, false);
// Clipper seems to reverse the polylines resulting from the difference operation. Revert the reversion.
for (auto it = path_fragments.back().polylines.begin(); it != path_fragments.back().polylines.end(); ++it)
std::reverse(it->points.begin(), it->points.end());
// Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter).
assert(this_layer.print_z > overlapping_layer.print_z);
frag.height = float(this_layer.print_z - overlapping_layer.print_z);
frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f, false).mm3_per_mm();
#ifdef SLIC3R_DEBUG
svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1));
#endif /* SLIC3R_DEBUG */
}
#ifdef SLIC3R_DEBUG
svg.draw(path_fragments.back().polylines, dbg_index_to_color(-1), scale_(0.1));
svg.Close();
#endif /* SLIC3R_DEBUG */
// Now chain the split segments using hashing and a nearly exact match, maintaining the order of segments.
// Create a single ExtrusionPath or ExtrusionEntityCollection per source ExtrusionPath.
// Map of fragment start/end points to a pair of <i_overlapping_layer, i_polyline_in_layer>
// Because a non-exact matching is used for the end points, a multi-map is used.
// As the clipper library may reverse the order of some clipped paths, store both ends into the map.
struct ExtrusionPathFragmentEnd
{
ExtrusionPathFragmentEnd(size_t alayer_idx, size_t apolyline_idx, bool ais_start) :
layer_idx(alayer_idx), polyline_idx(apolyline_idx), is_start(ais_start) {}
size_t layer_idx;
size_t polyline_idx;
bool is_start;
};
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) {
const Polylines &polylines = path_fragments[i_overlapping_layer].polylines;
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>
if (polylines[i_polyline].points.size() >= 2) {
const Point &pt_start = polylines[i_polyline].points.front();
const Point &pt_end = polylines[i_polyline].points.back();
map_fragment_starts.emplace(
std::make_pair(Point(pt_start.x>>4, pt_start.y>>4),
ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, true)));
map_fragment_starts.emplace(
std::make_pair(Point(pt_end.x>>4, pt_end.y>>4),
ExtrusionPathFragmentEnd(i_overlapping_layer, i_polyline, false)));
}
}
}
// For each source path:
for (size_t i_path = 0; i_path < path_ends.size(); ++ i_path) {
const Point &pt_start = path_ends[i_path].first;
const Point &pt_end = path_ends[i_path].second;
Point pt_current = pt_start;
// Find a chain of fragments with the original / reduced print height.
ExtrusionEntityCollection eec;
for (;;) {
// Iterate over 4 closest grid cells around pt_current,
// find the closest start point inside these cells to pt_current.
ExtrusionPathFragmentEnd fragment_end_min(size_t(-1), size_t(-1), false);
double dist_min = std::numeric_limits<double>::max();
// Round pt_current to a closest grid_cell corner.
Point grid_corner((pt_current.x+8)>>4, (pt_current.y+8)>>4);
// For four neighbors of grid_corner:
for (coord_t neighbor_y = -1; neighbor_y < 1; ++ neighbor_y) {
for (coord_t neighbor_x = -1; neighbor_x < 1; ++ neighbor_x) {
// Range of fragment starts around grid_corner, close to pt_current.
auto range = map_fragment_starts.equal_range(Point(grid_corner.x + neighbor_x, grid_corner.y + neighbor_y));
// Find the fragment start closest to the current
for (auto it = range.first; it != range.second; ++it) {
const ExtrusionPathFragmentEnd &fragment_end = it->second;
const Polyline &polyline = path_fragments[fragment_end.layer_idx].polylines[fragment_end.polyline_idx];
if (polyline.points.empty())
// This segment has been consumed.
continue;
const Point &pt = fragment_end.is_start ? polyline.points.front() : polyline.points.back();
const double d2 = pt_current.distance_to_sq(pt);
if (d2 < dist_min) {
dist_min = d2;
fragment_end_min = fragment_end;
}
}
}
}
if (dist_min == std::numeric_limits<double>::max()) {
// New fragment connecting to pt_current was not found.
// Verify that the last point found is close to the original end point of the unfragmented path.
const double d2 = pt_end.distance_to_sq(pt_current);
assert(d2 < 4 * 4);
// End of the path.
break;
}
// Fragment to consume.
ExtrusionPathFragment &frag = path_fragments[fragment_end_min.layer_idx];
Polyline &frag_polyline = frag.polylines[fragment_end_min.polyline_idx];
// Path to append the fragment to.
ExtrusionPath *path = eec.entities.empty() ? nullptr : dynamic_cast<ExtrusionPath*>(eec.entities.back());
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.
assert(path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm);
if (path->height != frag.height || path->mm3_per_mm != frag.mm3_per_mm)
path = nullptr;
}
if (path == nullptr) {
// Allocate a new path.
path = new ExtrusionPath(extrusion_role, frag.mm3_per_mm, frag.width, frag.height);
eec.entities.push_back(path);
}
// The Clipper library may flip the order of the clipped polylines arbitrarily.
// Reverse the source polyline, if connecting to the end.
if (! fragment_end_min.is_start)
frag_polyline.reverse();
// Enforce exact overlap of the end points of successive fragments.
frag_polyline.points.front() = pt_current;
// Don't repeat the first point.
if (! path->polyline.points.empty())
path->polyline.points.pop_back();
// Consume the fragment's polyline, remove it from the input fragments, so it will be ignored the next time.
path->polyline.append(std::move(frag_polyline));
frag_polyline.points.clear();
pt_current = path->polyline.points.back();
if (pt_current == pt_end) {
// End of the path.
break;
}
}
if (! eec.empty()) {
if (eec.entities.size() == 1) {
// This path was not fragmented.
extrusions_in_out.push_back(eec.entities.front());
eec.entities.pop_back();
} else {
// 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.
extrusions_in_out.push_back(new ExtrusionEntityCollection(std::move(eec)));
}
}
}
}
void PrintObjectSupportMaterial::generate_toolpaths( void PrintObjectSupportMaterial::generate_toolpaths(
const PrintObject &object, const PrintObject &object,
const MyLayersPtr &raft_layers, const MyLayersPtr &raft_layers,
@ -1847,13 +2102,29 @@ void PrintObjectSupportMaterial::generate_toolpaths(
filler_interface->spacing = m_support_material_interface_flow.spacing(); filler_interface->spacing = m_support_material_interface_flow.spacing();
fill_expolygons_generate_paths( fill_expolygons_generate_paths(
// Destination // Destination
support_layer.support_fills.entities, bottom_contact_layer.extrusions,
// Regions to fill // Regions to fill
union_ex(bottom_contact_layer.layer->polygons, true), union_ex(bottom_contact_layer.layer->polygons, true),
// Filler and its parameters // Filler and its parameters
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