Snapped the bottom interface layer print heights to the top interface

layer print heights to avoid too thin layer surfaces. The minimum layer
height at the nozzle page is maintained for the support layers.

Base layers are trimmed by the briding bottom surfaces.
This commit is contained in:
bubnikv 2017-03-28 13:46:31 +02:00
parent 9f7a5c7a6f
commit 40b75f6cee
2 changed files with 121 additions and 66 deletions

View file

@ -20,8 +20,7 @@
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
//#ifdef SLIC3R_DEBUG
#if 1
#ifdef SLIC3R_DEBUG
#define DEBUG
#define _DEBUG
#undef NDEBUG
@ -171,9 +170,13 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
float(slicing_params.layer_height),
false)),
// 50 mirons layer
m_support_layer_height_min (0.05)
m_support_layer_height_min (0.01)
{
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
m_support_layer_height_min = 1000000.;
for (auto lh : m_print_config->min_layer_height.values)
m_support_layer_height_min = std::min(m_support_layer_height_min, std::max(0.01, lh));
if (m_object_config->support_material_interface_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
m_support_material_interface_flow = m_support_material_flow;
@ -602,8 +605,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Collect perimeters of this layer.
// TODO: split_at_first_point() could split a bridge mid-way
Polylines overhang_perimeters;
for (ExtrusionEntitiesPtr::const_iterator it_island = layerm.perimeters.entities.begin(); it_island != layerm.perimeters.entities.end(); ++ it_island) {
const ExtrusionEntityCollection *island = dynamic_cast<ExtrusionEntityCollection*>(*it_island);
for (ExtrusionEntity* extrusion_entity : layerm.perimeters.entities) {
const ExtrusionEntityCollection *island = dynamic_cast<ExtrusionEntityCollection*>(extrusion_entity);
assert(island != NULL);
for (size_t i = 0; i < island->entities.size(); ++ i) {
ExtrusionEntity *entity = island->entities[i];
@ -615,8 +618,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
}
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it)
it->points[0].x += 1;
for (Polyline &polyline : overhang_perimeters)
polyline.points[0].x += 1;
// Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
@ -627,23 +630,23 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// so we take the largest value and also apply safety offset to be ensure no gaps
// are left in between
float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
for (Polylines::iterator it = overhang_perimeters.begin(); it != overhang_perimeters.end(); ++ it) {
if (it->is_straight()) {
for (Polyline &polyline : overhang_perimeters)
if (polyline.is_straight()) {
// This is a bridge
it->extend_start(fw);
it->extend_end(fw);
if (layer.slices.contains(it->first_point()) && layer.slices.contains(it->last_point()))
// Offset a polyline into a polygon.
polygons_append(bridged_perimeters, offset(*it, 0.5f * w + 10.f));
polyline.extend_start(fw);
polyline.extend_end(fw);
// Is the straight perimeter segment supported at both sides?
if (layer.slices.contains(polyline.first_point()) && layer.slices.contains(polyline.last_point()))
// Offset a polyline into a thick line.
polygons_append(bridged_perimeters, offset(polyline, 0.5f * w + 10.f));
}
}
bridged_perimeters = union_(bridged_perimeters);
}
// remove the entire bridges and only support the unsupported edges
Polygons bridges;
for (Surfaces::const_iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it)
if (it->surface_type == stBottomBridge && it->bridge_angle != -1)
polygons_append(bridges, it->expolygon);
for (const Surface &surface : layerm.fill_surfaces.surfaces)
if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
polygons_append(bridges, surface.expolygon);
diff_polygons = diff(diff_polygons, bridges, true);
polygons_append(bridges, bridged_perimeters);
polygons_append(diff_polygons,
@ -733,15 +736,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// In the future we may switch to a normal extrusion flow for the supported bridges.
// Get the average nozzle diameter used on this layer.
coordf_t nozzle_dmr = 0.;
size_t n_nozzle_dmrs = 0;
for (LayerRegionPtrs::const_iterator it_region_ptr = layer.regions.begin(); it_region_ptr != layer.regions.end(); ++ it_region_ptr) {
const PrintRegion &region = *(*it_region_ptr)->region();
nozzle_dmr += m_print_config->nozzle_diameter.get_at(region.config.perimeter_extruder.value - 1);
nozzle_dmr += m_print_config->nozzle_diameter.get_at(region.config.infill_extruder.value - 1);
nozzle_dmr += m_print_config->nozzle_diameter.get_at(region.config.solid_infill_extruder.value - 1);
n_nozzle_dmrs += 3;
}
nozzle_dmr /= coordf_t(n_nozzle_dmrs);
for (const LayerRegion *region : layer.regions)
nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config);
nozzle_dmr /= coordf_t(layer.regions.size());
new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance;
new_layer.bottom_z = new_layer.print_z;
new_layer.height = 0.;
@ -899,7 +896,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// 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] {
task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] {
Polygons top = collect_region_slices_by_type(layer, stTop);
#ifdef SLIC3R_DEBUG
{
@ -938,9 +935,36 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
layer_new.bridging = ! m_slicing_params.soluble_interface;
//FIXME how much to inflate the top surface?
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! m_slicing_params.soluble_interface) {
// Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
// so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
for (size_t top_idx = size_t(std::max<int>(0, contact_idx));
top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min;
++ top_idx) {
if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min) {
// A top layer has been found, which is close to the new bottom layer.
coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z;
assert(std::abs(diff) <= this->m_support_layer_height_min);
if (diff > 0.) {
// The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
assert(diff < layer_new.height + EPSILON);
assert(layer_new.height - diff >= this->m_support_layer_height_min - EPSILON);
layer_new.print_z = top_contacts[top_idx]->print_z;
layer_new.height -= diff;
} else {
// The top contact layer is above this layer. One may either make this layer thicker or thinner.
// By making the layer thicker, one will decrease the number of discrete layers with the price of extruding a bit too thick bridges.
// By making the layer thinner, one adds one more discrete layer.
layer_new.print_z = top_contacts[top_idx]->print_z;
layer_new.height -= diff;
}
break;
}
}
}
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z),
debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z),
union_ex(layer_new.polygons, false));
#endif /* SLIC3R_DEBUG */
// Trim the already created base layers above the current layer intersecting with the bottom contacts layer.
@ -1108,21 +1132,6 @@ void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts(
});
}
// A helper for sorting the top / bottom contact layers by their contact with the touching support layer:
// Top contact surfaces (those supporting overhangs) are sorted by their bottom print Z,
// bottom contact surfaces (those supported by top object surfaces) are sorted by their top print Z.
struct LayerExtreme
{
LayerExtreme(PrintObjectSupportMaterial::MyLayer *alayer, bool ais_top) : layer(alayer), is_top(ais_top) {}
PrintObjectSupportMaterial::MyLayer *layer;
// top or bottom extreme
bool is_top;
coordf_t z() const { return is_top ? layer->print_z : layer->print_z - layer->height; }
bool operator<(const LayerExtreme &other) const { return z() < other.z(); }
};
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_support_layers(
const PrintObject &object,
const MyLayersPtr &bottom_contacts,
@ -1132,52 +1141,71 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
MyLayersPtr intermediate_layers;
// Collect and sort the extremes (bottoms of the top contacts and tops of the bottom contacts).
std::vector<LayerExtreme> extremes;
MyLayersPtr extremes;
extremes.reserve(top_contacts.size() + bottom_contacts.size());
for (size_t i = 0; i < top_contacts.size(); ++ i)
// Bottoms of the top contact layers. In case of non-soluble supports,
// the top contact layer thickness is not known yet.
extremes.push_back(LayerExtreme(top_contacts[i], false));
extremes.push_back(top_contacts[i]);
for (size_t i = 0; i < bottom_contacts.size(); ++ i)
// Tops of the bottom contact layers.
extremes.push_back(LayerExtreme(bottom_contacts[i], true));
extremes.push_back(bottom_contacts[i]);
if (extremes.empty())
return intermediate_layers;
std::sort(extremes.begin(), extremes.end());
auto layer_extreme_lower = [](const MyLayer *l1, const MyLayer *l2) {
coordf_t z1 = l1->extreme_z();
coordf_t z2 = l2->extreme_z();
// If the layers are aligned, return the top contact surface first.
return z1 < z2 || (z1 == z2 && l1->layer_type == PrintObjectSupportMaterial::sltTopContact && l2->layer_type == PrintObjectSupportMaterial::sltBottomContact);
};
std::sort(extremes.begin(), extremes.end(), layer_extreme_lower);
assert(extremes.empty() ||
(extremes.front().z() > m_slicing_params.raft_interface_top_z - EPSILON &&
(extremes.front()->extreme_z() > m_slicing_params.raft_interface_top_z - EPSILON &&
(m_slicing_params.raft_layers() == 1 || // only raft contact layer
extremes.front().layer->layer_type == sltTopContact || // first extreme is a top contact layer
extremes.front().z() > m_slicing_params.first_print_layer_height - EPSILON)));
extremes.front()->layer_type == sltTopContact || // first extreme is a top contact layer
extremes.front()->extreme_z() > m_slicing_params.first_print_layer_height - EPSILON)));
// bool synchronize = m_slicing_params.soluble_interface || this->synchronize_layers();
bool synchronize = this->synchronize_layers();
#ifdef _DEBUG
// Verify that the extremes are separated by m_support_layer_height_min.
for (size_t i = 1; i < extremes.size(); ++ i) {
assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() == 0. ||
extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > this->m_support_layer_height_min - EPSILON);
assert(extremes[i]->extreme_z() - extremes[i-1]->extreme_z() > 0. ||
extremes[i]->layer_type == extremes[i-1]->layer_type ||
(extremes[i]->layer_type == sltBottomContact && extremes[i - 1]->layer_type == sltTopContact));
}
#endif
// Generate intermediate layers.
// The first intermediate layer is the same as the 1st layer if there is no raft,
// or the bottom of the first intermediate layer is aligned with the bottom of the raft contact layer.
// Intermediate layers are always printed with a normal etrusion flow (non-bridging).
size_t idx_layer_object = 0;
for (size_t idx_extreme = 0; idx_extreme < extremes.size(); ++ idx_extreme) {
LayerExtreme &extr2 = extremes[idx_extreme];
coordf_t extr2z = extr2.z();
MyLayer *extr2 = extremes[idx_extreme];
coordf_t extr2z = extr2->extreme_z();
if (std::abs(extr2z - m_slicing_params.raft_interface_top_z) < EPSILON) {
// This is a raft contact layer, its height has been decided in this->top_contact_layers().
assert(extr2.layer->layer_type == sltTopContact);
assert(extr2->layer_type == sltTopContact);
continue;
}
if (std::abs(extr2z - m_slicing_params.first_print_layer_height) < EPSILON) {
// This is a 1st layer supporting some of the early object print layers, its height has been decided in this->top_contact_layers().
assert(extr2.layer->layer_type == sltTopContact);
assert(extr2->layer_type == sltTopContact);
continue;
}
assert(extr2z >= m_slicing_params.raft_interface_top_z + EPSILON);
assert(extr2z >= m_slicing_params.first_print_layer_height + EPSILON);
LayerExtreme *extr1 = (idx_extreme == 0) ? nullptr : &extremes[idx_extreme - 1];
MyLayer *extr1 = (idx_extreme == 0) ? nullptr : extremes[idx_extreme - 1];
// Fuse a support layer firmly to the raft top interface (not to the raft contacts).
coordf_t extr1z = (extr1 == nullptr) ? m_slicing_params.raft_interface_top_z : extr1->z();
assert(extr2z > extr1z + EPSILON);
coordf_t extr1z = (extr1 == nullptr) ? m_slicing_params.raft_interface_top_z : extr1->extreme_z();
assert(extr2z >= extr1z);
assert(extr2z > extr1z || (extr1 != nullptr && extr2->layer_type == sltBottomContact));
if (std::abs(extr1z) < EPSILON) {
// This layer interval starts with the 1st layer. Print the 1st layer using the prescribed 1st layer thickness.
assert(! m_slicing_params.has_raft());
@ -1196,16 +1224,18 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::raft_and_int
assert(dist >= 0.);
if (dist == 0.)
continue;
// The new layers shall be at least m_support_layer_height_min thick.
assert(dist >= m_support_layer_height_min - EPSILON);
// Insert intermediate layers.
size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height));
assert(n_layers_extra > 0);
coordf_t step = dist / coordf_t(n_layers_extra);
if (! synchronize && ! m_slicing_params.soluble_interface && extr2.layer->layer_type == sltTopContact) {
if (! synchronize && ! m_slicing_params.soluble_interface && extr2->layer_type == sltTopContact) {
// This is a top interface layer, which does not have a height assigned yet. Do it now.
assert(extr2.layer->height == 0.);
assert(extr2->height == 0.);
assert(extr1z > m_slicing_params.first_print_layer_height - EPSILON);
extr2.layer->height = step;
extr2.layer->bottom_z = extr2z = extr2.layer->print_z - step;
extr2->height = step;
extr2->bottom_z = extr2z = extr2->print_z - step;
if (-- n_layers_extra == 0)
continue;
}
@ -1440,13 +1470,29 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
[z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; });
// Collect all the object layers intersecting with this layer.
Polygons polygons_trimming;
for (size_t i = idx_object_layer_overlapping; i < object.layers.size(); ++ i) {
size_t i = idx_object_layer_overlapping;
for (; 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);
}
if (! this->m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
for (; i < object.layers.size(); ++ i) {
const Layer &object_layer = *object.layers[i];
bool some_region_overlaps = false;
for (LayerRegion* region : object_layer.regions) {
coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*this->m_print_config);
if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON)
break;
some_region_overlaps = true;
polygons_append(polygons_trimming, to_polygons(region->slices.filter_by_type(stBottomBridge)));
}
if (! some_region_overlaps)
break;
}
}
// $layer->slices contains the full shape of layer, thus including
// perimeter's width. $support contains the full shape of support
// material, thus including the width of its foremost extrusion.
@ -2526,7 +2572,11 @@ 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(float(m_support_material_flow.width), float(base_layer.layer->height), m_support_material_flow.nozzle_diameter, base_layer.layer->bridging);
Flow flow(
float(base_layer.layer->bridging ? base_layer.layer->height : 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 = coord_t(scale_(filler->spacing * link_max_length_factor / support_density));
float density = float(support_density);

View file

@ -91,8 +91,13 @@ public:
return false;
}
// For the bridging flow, bottom_print_z will be above bottom_z to account for the vertical separation.
// For the non-bridging flow, bottom_print_z will be equal to bottom_z.
coordf_t bottom_print_z() const { return print_z - height; }
// To sort the extremes of top / bottom interface layers.
coordf_t extreme_z() const { return (this->layer_type == sltTopContact) ? this->bottom_z : this->print_z; }
SupporLayerType layer_type;
// Z used for printing, in unscaled coordinates.
coordf_t print_z;