New ClipperUtils functions: opening(), closing() as an alternative

for offset2() with clear meaning.
New ClipperUtils functions: expand(), shrink() as an alternative
for offset() with clear meaning.
All offset values for the new functions are positive.

Various offsetting ClipperUtils (offset, offset2, offset2_ex) working
over Polygons were marked as unsafe, sometimes producing invalid output
if called for more than one polygon. These functions were reworked
to offset polygons one by one. The new functions working over Polygons
shall work the same way as the old safe ones working over ExPolygons,
but working with Polygons shall be computationally more efficient.

Improvements in FDM support generator:
1) For both grid and snug supports: Don't filter out supports for which
   the contacts are completely reduced by support / object XY separation.
2) Rounding / merging of supports using the closing radius parameter is
   now smoother, it does not produce sharp corners.
3) Snug supports: When calculating support interfaces, expand the projected
   support contact areas to produce wider, printable and more stable interfaces.
4) Don't reduce support interfaces for snug supports for steep overhangs,
   that would normally not need them. Snug supports often produce very
   narrow support interface regions and turning them off makes the support
   interfaces disappear.
This commit is contained in:
Vojtech Bubnik 2021-10-14 09:11:19 +02:00
parent 2f9ce6bedb
commit 7ff76d0768
19 changed files with 480 additions and 354 deletions
src/libslic3r

View file

@ -367,6 +367,29 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
// Object is printed with the same extruder as the support.
m_support_params.can_merge_support_regions = true;
}
m_support_params.base_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value));
m_support_params.interface_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.));
m_support_params.interface_spacing = m_object_config->support_material_interface_spacing.value + m_support_params.support_material_interface_flow.spacing();
m_support_params.interface_density = std::min(1., m_support_params.support_material_interface_flow.spacing() / m_support_params.interface_spacing);
m_support_params.support_spacing = m_object_config->support_material_spacing.value + m_support_params.support_material_flow.spacing();
m_support_params.support_density = std::min(1., m_support_params.support_material_flow.spacing() / m_support_params.support_spacing);
if (m_object_config->support_material_interface_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
m_support_params.interface_spacing = m_support_params.support_spacing;
m_support_params.interface_density = m_support_params.support_density;
}
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
m_support_params.with_sheath = m_object_config->support_material_with_sheath;
m_support_params.base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (m_support_params.support_density > 0.95 ? ipRectilinear : ipSupportBase);
m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
m_support_params.contact_fill_pattern =
(m_object_config->support_material_interface_pattern == smipAuto && m_slicing_params.soluble_interface) ||
m_object_config->support_material_interface_pattern == smipConcentric ?
ipConcentric :
(m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
}
// Using the std::deque as an allocator.
@ -883,7 +906,14 @@ public:
// Merge the support polygons by applying morphological closing and inwards smoothing.
auto closing_distance = scaled<float>(m_support_material_closing_radius);
auto smoothing_distance = scaled<float>(m_extrusion_width);
return smooth_outward(offset(offset_ex(*m_support_polygons, closing_distance), - closing_distance), smoothing_distance);
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z),
{ { { diff_ex(expand(*m_support_polygons, closing_distance), closing(*m_support_polygons, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS)) }, { "closed", "blue", 0.5f } },
{ { union_ex(smooth_outward(closing(*m_support_polygons, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance)) }, { "regularized", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } },
{ { union_ex(*m_support_polygons) }, { "src", "green", 0.5f } },
});
#endif /* SLIC3R_DEBUG */
return smooth_outward(closing(*m_support_polygons, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance);
}
assert(false);
return Polygons();
@ -1250,7 +1280,7 @@ namespace SupportMaterialInternal {
Polygons bridges;
{
// Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
Polygons lower_grown_slices = offset(lower_layer_polygons,
Polygons lower_grown_slices = expand(lower_layer_polygons,
//FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm.region().config().perimeter_extruder-1))),
SUPPORT_SURFACES_OFFSET_PARAMETERS);
@ -1414,7 +1444,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
overhang_polygons = to_polygons(layer.lslices);
#endif
// Expand for better stability.
contact_polygons = offset(overhang_polygons, scaled<float>(object_config.raft_expansion.value));
contact_polygons = expand(overhang_polygons, scaled<float>(object_config.raft_expansion.value));
}
else if (! layer.regions().empty())
{
@ -1475,20 +1505,20 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
//FIXME cache the lower layer offset if this layer has multiple regions.
#if 0
//FIXME this solution will trigger stupid supports for sharp corners, see GH #4874
diff_polygons = offset2(
diff_polygons = opening(
diff(layerm_polygons,
// Likely filtering out thin regions from the lower layer, that will not be covered by perimeters, thus they
// are not supporting this layer.
// However this may lead to a situation where regions at the current layer that are narrow thus not extrudable will generate unnecessary supports.
// For example, see GH issue #3094
offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
//FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
opening(lower_layer_polygons, 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
//FIXME This opening is targeted to reduce very thin regions to support, but it may lead to
// no support at all for not so steep overhangs.
- 0.1f * fw, 0.1f * fw);
0.1f * fw);
#else
diff_polygons =
diff(layerm_polygons,
offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
expand(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
#endif
if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) {
// Don't support overhangs above the top surfaces.
@ -1500,7 +1530,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
// This is done to increase size of the supporting columns below, as they are calculated by
// propagating these contact surfaces downwards.
diff_polygons = diff(
intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
lower_layer_polygons);
}
//FIXME add user defined filtering here based on minimal area or minimum radius or whatever.
@ -1516,7 +1546,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
// Subtracting them as they are may leave unwanted narrow
// residues of diff_polygons that would then be supported.
diff_polygons = diff(diff_polygons,
offset(union_(annotations.blockers_layers[layer_id]), float(1000.*SCALED_EPSILON)));
expand(union_(annotations.blockers_layers[layer_id]), float(1000.*SCALED_EPSILON)));
}
#ifdef SLIC3R_DEBUG
@ -1588,7 +1618,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
#endif // SLIC3R_DEBUG
enforcer_polygons = diff(intersection(layer.lslices, annotations.enforcers_layers[layer_id]),
// Inflate just a tiny bit to avoid intersection of the overhang areas with the object.
offset(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
expand(lower_layer_polygons, 0.05f * no_interface_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-top-contacts-enforcers-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { layer.lslices, { "layer.lslices", "gray", 0.2f } },
@ -1720,7 +1750,7 @@ static inline void fill_contact_layer(
if (lower_layer_polygons_for_dense_interface_cache.empty())
lower_layer_polygons_for_dense_interface_cache =
//FIXME no_interface_offset * 0.6f offset is not quite correct, one shall derive it based on an angle thus depending on layer height.
offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS);
opening(lower_layer_polygons, no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS);
return lower_layer_polygons_for_dense_interface_cache;
};
@ -1733,7 +1763,7 @@ static inline void fill_contact_layer(
#endif // SLIC3R_DEBUG
));
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
bool reduce_interfaces = layer_id > 0 && ! slicing_params.soluble_interface;
bool reduce_interfaces = object_config.support_material_style.value != smsSnug && layer_id > 0 && !slicing_params.soluble_interface;
if (reduce_interfaces) {
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
Polygons dense_interface_polygons = diff(overhang_polygons, lower_layer_polygons_for_dense_interface());
@ -1741,7 +1771,7 @@ static inline void fill_contact_layer(
dense_interface_polygons =
diff(
// Regularize the contour.
offset(dense_interface_polygons, no_interface_offset * 0.1f),
expand(dense_interface_polygons, no_interface_offset * 0.1f),
slices_margin.polygons);
// Support islands, to be stretched into a grid.
//FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons,
@ -1799,7 +1829,7 @@ static inline void fill_contact_layer(
dense_interface_polygons =
diff(
// Regularize the contour.
offset(dense_interface_polygons, no_interface_offset * 0.1f),
expand(dense_interface_polygons, no_interface_offset * 0.1f),
slices_margin.all_polygons);
// Support islands, to be stretched into a grid.
//FIXME The regularization of dense_interface_polygons above may stretch dense_interface_polygons outside of the contact polygons,
@ -1937,7 +1967,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
);
// Now apply the contact areas to the layer where they need to be made.
if (! contact_polygons.empty()) {
if (! contact_polygons.empty() || ! overhang_polygons.empty()) {
// Allocate the two empty layers.
auto [new_layer, bridging_layer] = new_contact_layer(*m_print_config, *m_object_config, m_slicing_params, m_support_params.support_layer_height_min, layer, layer_storage, layer_storage_mutex);
if (new_layer) {
@ -2041,8 +2071,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
layer_new.idx_object_layer_below = layer_id;
layer_new.bridging = !slicing_params.soluble_interface && object.config().thick_bridges;
//FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
//FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
layer_new.polygons = offset(touching, float(support_params.support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
layer_new.polygons = expand(touching, float(support_params.support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! slicing_params.soluble_interface) {
// Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
@ -2081,7 +2110,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
// Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
//FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
touching = offset(touching, float(SCALED_EPSILON));
touching = expand(touching, float(SCALED_EPSILON));
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
const Layer &layer_above = *object.layers()[layer_id_above];
if (layer_above.print_z > layer_new.print_z - EPSILON)
@ -2249,7 +2278,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
#endif
// 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.
polygons_append(polygons_new, offset(*top_contact.overhang_polygons, float(SCALED_EPSILON)));
polygons_append(polygons_new, expand(*top_contact.overhang_polygons, float(SCALED_EPSILON)));
polygons_append(overhangs_projection, union_(polygons_new));
polygons_append(enforcers_projection, enforcers_new);
}
@ -2736,7 +2765,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
ApplySafetyOffset::Yes); // safety offset to merge the touching source polygons
layer_intermediate.layer_type = sltBase;
// For snug supports, expand the interfaces into the intermediate layer to make it printable.
#if 0
// coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing);
// Fillet the base polygons and trim them again with the top, interface and contact layers.
@ -2868,7 +2896,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
if (brim_inner) {
Polygons holes = ex.holes;
polygons_reverse(holes);
holes = offset(holes, - brim_separation, ClipperLib::jtRound, float(scale_(0.1)));
holes = shrink(holes, brim_separation, ClipperLib::jtRound, float(scale_(0.1)));
polygons_reverse(holes);
polygons_append(brim, std::move(holes));
} else
@ -2900,11 +2928,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
Polygons interface_polygons;
if (contacts != nullptr && ! contacts->polygons.empty())
polygons_append(interface_polygons, offset(contacts->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
polygons_append(interface_polygons, expand(contacts->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (interfaces != nullptr && ! interfaces->polygons.empty())
polygons_append(interface_polygons, offset(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
polygons_append(interface_polygons, expand(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (base_interfaces != nullptr && ! base_interfaces->polygons.empty())
polygons_append(interface_polygons, offset(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
polygons_append(interface_polygons, expand(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
// Output vector.
MyLayersPtr raft_layers;
@ -2931,7 +2959,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
new_layer.print_z = m_slicing_params.first_print_layer_height;
new_layer.height = m_slicing_params.first_print_layer_height;
new_layer.bottom_z = 0.;
new_layer.polygons = inflate_factor_1st_layer > 0 ? offset(base, inflate_factor_1st_layer) : base;
new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(base, inflate_factor_1st_layer) : base;
}
// Insert the base layers.
for (size_t i = 1; i < m_slicing_params.base_raft_layers; ++ i) {
@ -2965,7 +2993,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / m_support_params.first_layer_flow.scaled_width())));
float step = inflate_factor_1st_layer / nsteps;
for (int i = 0; i < nsteps; ++ i)
raft = diff(offset(raft, step), trimming);
raft = diff(expand(raft, step), trimming);
} else
raft = diff(raft, trimming);
if (contacts != nullptr)
@ -3028,26 +3056,44 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
interface_layers.assign(intermediate_layers.size(), nullptr);
if (num_base_interface_layers_top || num_base_interface_layers_bottom)
base_interface_layers.assign(intermediate_layers.size(), nullptr);
bool snug_supports = m_object_config->support_material_style.value == smsSnug;
auto smoothing_distance = m_support_params.support_material_interface_flow.scaled_spacing() * 1.5;
auto minimum_island_radius = m_support_params.support_material_interface_flow.scaled_spacing() / m_support_params.interface_density;
auto closing_distance = smoothing_distance; // scaled<float>(m_object_config->support_material_closing_radius.value);
tbb::spin_mutex layer_storage_mutex;
// Insert a new layer into base_interface_layers, if intersection with base exists.
auto insert_layer = [&layer_storage, &layer_storage_mutex](MyLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) {
auto insert_layer = [&layer_storage, &layer_storage_mutex, snug_supports, closing_distance, smoothing_distance, minimum_island_radius](
MyLayer &intermediate_layer, Polygons &bottom, Polygons &&top, const Polygons *subtract, SupporLayerType type) -> MyLayer* {
assert(! bottom.empty() || ! top.empty());
MyLayer &layer_new = layer_allocate(layer_storage, layer_storage_mutex, type);
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;
// Merge top into bottom, unite them with a safety offset.
append(bottom, std::move(top));
layer_new.polygons = intersection(union_safety_offset(std::move(bottom)), intermediate_layer.polygons);
// Subtract the interface from the base regions.
intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons);
if (subtract)
// Trim the base interface layer with the interface layer.
layer_new.polygons = diff(std::move(layer_new.polygons), *subtract);
//FIXME filter layer_new.polygons islands by a minimum area?
// $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
return &layer_new;
// Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners).
bottom = intersection(
snug_supports ?
smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) :
union_safety_offset(std::move(bottom)),
intermediate_layer.polygons);
if (! bottom.empty()) {
//FIXME Remove non-printable tiny islands, let them be printed using the base support.
//bottom = offset(offset_ex(std::move(bottom), - minimum_island_radius), minimum_island_radius);
if (! bottom.empty()) {
MyLayer &layer_new = layer_allocate(layer_storage, layer_storage_mutex, type);
layer_new.polygons = std::move(bottom);
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;
// Subtract the interface from the base regions.
intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons);
if (subtract)
// Trim the base interface layer with the interface layer.
layer_new.polygons = diff(std::move(layer_new.polygons), *subtract);
//FIXME filter layer_new.polygons islands by a minimum area?
// $interface_area = [ grep abs($_->area) >= $area_threshold, @$interface_area ];
return &layer_new;
}
}
return nullptr;
};
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
[&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer,
@ -3196,7 +3242,7 @@ static inline void fill_expolygons_with_sheath_generate_paths(
return;
if (! with_sheath) {
fill_expolygons_generate_paths(dst, offset2_ex(polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON)), filler, density, role, flow);
fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow);
return;
}
@ -3208,7 +3254,7 @@ static inline void fill_expolygons_with_sheath_generate_paths(
// Clip the sheath path to avoid the extruder to get exactly on the first point of the loop.
double clip_length = spacing * 0.15;
for (ExPolygon &expoly : offset2_ex(polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON - 0.5*flow.scaled_width()))) {
for (ExPolygon &expoly : closing_ex(polygons, float(SCALED_EPSILON), float(SCALED_EPSILON + 0.5*flow.scaled_width()))) {
// Don't reorder the skirt and its infills.
std::unique_ptr<ExtrusionEntityCollection> eec;
if (no_sort) {
@ -3461,10 +3507,10 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
// make more loops
Polygons loop_polygons = loops0;
for (int i = 1; i < n_contact_loops; ++ i)
polygons_append(loop_polygons,
offset2(
polygons_append(loop_polygons,
opening(
loops0,
- i * flow.scaled_spacing() - 0.5f * flow.scaled_spacing(),
i * flow.scaled_spacing() + 0.5f * flow.scaled_spacing(),
0.5f * flow.scaled_spacing()));
// Clip such loops to the side oriented towards the object.
// Collect split points, so they will be recognized after the clipping.
@ -3476,7 +3522,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
map_split_points[it->first_point()] = -1;
loop_lines.push_back(it->split_at_first_point());
}
loop_lines = intersection_pl(loop_lines, offset(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)));
loop_lines = intersection_pl(loop_lines, expand(overhang_polygons, scale_(SUPPORT_MATERIAL_MARGIN)));
// Because a closed loop has been split to a line, loop_lines may contain continuous segments split to 2 pieces.
// Try to connect them.
for (int i_line = 0; i_line < int(loop_lines.size()); ++ i_line) {
@ -3816,27 +3862,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_params.support_material_interface_flow.scaled_width());
loop_interface_processor.n_contact_loops = this->has_contact_loops() ? 1 : 0;
float base_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value));
float interface_angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.));
coordf_t interface_spacing = m_object_config->support_material_interface_spacing.value + m_support_params.support_material_interface_flow.spacing();
coordf_t interface_density = std::min(1., m_support_params.support_material_interface_flow.spacing() / interface_spacing);
coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_params.support_material_flow.spacing();
coordf_t support_density = std::min(1., m_support_params.support_material_flow.spacing() / support_spacing);
if (m_object_config->support_material_interface_layers.value == 0) {
// No interface layers allowed, print everything with the base support pattern.
interface_spacing = support_spacing;
interface_density = support_density;
}
// Prepare fillers.
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
bool with_sheath = m_object_config->support_material_with_sheath;
InfillPattern infill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : (support_density > 0.95 ? ipRectilinear : ipSupportBase);
std::vector<float> angles;
angles.push_back(base_angle);
if (support_pattern == smpRectilinearGrid)
angles.push_back(interface_angle);
std::vector<float> angles { m_support_params.base_angle };
if (m_object_config->support_material_pattern == smpRectilinearGrid)
angles.push_back(m_support_params.interface_angle);
BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.)));
@ -3848,16 +3876,16 @@ void PrintObjectSupportMaterial::generate_toolpaths(
float raft_angle_interface = 0.f;
if (m_slicing_params.base_raft_layers > 1) {
// There are all raft layer types (1st layer, base, interface & contact layers) available.
raft_angle_1st_layer = interface_angle;
raft_angle_base = base_angle;
raft_angle_interface = interface_angle;
raft_angle_1st_layer = m_support_params.interface_angle;
raft_angle_base = m_support_params.base_angle;
raft_angle_interface = m_support_params.interface_angle;
} else if (m_slicing_params.base_raft_layers == 1 || m_slicing_params.interface_raft_layers > 1) {
// 1st layer, interface & contact layers available.
raft_angle_1st_layer = base_angle;
raft_angle_1st_layer = m_support_params.base_angle;
if (this->has_support())
// Print 1st layer at 45 degrees from both the interface and base angles as both can land on the 1st layer.
raft_angle_1st_layer += 0.7854f;
raft_angle_interface = interface_angle;
raft_angle_interface = m_support_params.interface_angle;
} else if (m_slicing_params.interface_raft_layers == 1) {
// Only the contact raft layer is non-empty, which will be printed as the 1st layer.
assert(m_slicing_params.base_raft_layers == 0);
@ -3874,7 +3902,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
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, &support_layers, &raft_layers,
infill_pattern, &bbox_object, support_density, interface_density, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor, with_sheath]
&bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor]
(const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{
@ -3883,8 +3911,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
assert(support_layer.support_fills.entities.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));
std::unique_ptr<Fill> filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.interface_fill_pattern));
std::unique_ptr<Fill> filler_support = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.base_fill_pattern));
filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
@ -3900,17 +3928,17 @@ void PrintObjectSupportMaterial::generate_toolpaths(
Fill * filler = filler_support.get();
filler->angle = raft_angle_base;
filler->spacing = m_support_params.support_material_flow.spacing();
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density));
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.support_density));
fill_expolygons_with_sheath_generate_paths(
// Destination
support_layer.support_fills.entities,
// Regions to fill
to_infill_polygons,
// Filler and its parameters
filler, float(support_density),
filler, float(m_support_params.support_density),
// Extrusion parameters
erSupportMaterial, flow,
with_sheath, false);
m_support_params.with_sheath, false);
}
}
@ -3929,7 +3957,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
filler->spacing = m_support_params.support_material_flow.spacing();
assert(! raft_layer.bridging);
flow = Flow(float(m_support_params.support_material_interface_flow.width()), float(raft_layer.height), m_support_params.support_material_flow.nozzle_diameter());
density = float(interface_density);
density = float(m_support_params.interface_density);
} else
continue;
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
@ -3970,15 +3998,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
};
std::vector<LayerCache> layer_caches(support_layers.size());
const auto fill_type_interface =
(m_object_config->support_material_interface_pattern == smipAuto && m_slicing_params.soluble_interface) ||
m_object_config->support_material_interface_pattern == smipConcentric ?
ipConcentric : ipRectilinear;
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, support_layers.size()),
[this, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &base_interface_layers, &layer_caches, &loop_interface_processor,
infill_pattern, &bbox_object, support_density, fill_type_interface, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath]
&bbox_object, &angles, link_max_length_factor]
(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 = size_t(-1);
@ -3987,14 +4009,14 @@ void PrintObjectSupportMaterial::generate_toolpaths(
size_t idx_layer_interface = size_t(-1);
size_t idx_layer_base_interface = size_t(-1);
const auto fill_type_first_layer = ipRectilinear;
auto filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(fill_type_interface));
auto filler_interface = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.contact_fill_pattern));
// Filler for the 1st layer interface, if different from filler_interface.
auto filler_first_layer_ptr = std::unique_ptr<Fill>(range.begin() == 0 && fill_type_interface != fill_type_first_layer ? Fill::new_from_type(fill_type_first_layer) : nullptr);
auto filler_first_layer_ptr = std::unique_ptr<Fill>(range.begin() == 0 && m_support_params.contact_fill_pattern != fill_type_first_layer ? Fill::new_from_type(fill_type_first_layer) : nullptr);
// Pointer to the 1st layer interface filler.
auto filler_first_layer = filler_first_layer_ptr ? filler_first_layer_ptr.get() : filler_interface.get();
// Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer).
auto filler_base_interface = std::unique_ptr<Fill>(base_interface_layers.empty() ? nullptr : Fill::new_from_type(ipRectilinear));
auto filler_support = std::unique_ptr<Fill>(Fill::new_from_type(infill_pattern));
auto filler_base_interface = std::unique_ptr<Fill>(base_interface_layers.empty() ? nullptr : Fill::new_from_type(m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase));
auto filler_support = std::unique_ptr<Fill>(Fill::new_from_type(m_support_params.base_fill_pattern));
filler_interface->set_bounding_box(bbox_object);
if (filler_first_layer_ptr)
filler_first_layer_ptr->set_bounding_box(bbox_object);
@ -4084,8 +4106,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// If zero interface layers are configured, use the same angle as for the base layers.
angles[support_layer_id % angles.size()] :
// Use interface angle for the interface layers.
interface_angle;
double density = interface_as_base ? support_density : interface_density;
m_support_params.interface_angle;
double density = interface_as_base ? m_support_params.support_density : m_support_params.interface_density;
filler_interface->spacing = interface_as_base ? m_support_params.support_material_flow.spacing() : m_support_params.support_material_interface_flow.spacing();
filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density));
fill_expolygons_generate_paths(
@ -4106,9 +4128,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
assert(! base_interface_layer.layer->bridging);
Flow interface_flow = m_support_params.support_material_flow.with_height(float(base_interface_layer.layer->height));
filler->angle = interface_angle;
filler->angle = m_support_params.interface_angle;
filler->spacing = m_support_params.support_material_interface_flow.spacing();
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / interface_density));
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.interface_density));
fill_expolygons_generate_paths(
// Destination
base_interface_layer.extrusions,
@ -4116,7 +4138,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Regions to fill
union_safety_offset_ex(base_interface_layer.polygons_to_extrude()),
// Filler and its parameters
filler, float(interface_density),
filler, float(m_support_params.interface_density),
// Extrusion parameters
erSupportMaterial, interface_flow);
}
@ -4130,9 +4152,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
assert(! base_layer.layer->bridging);
auto flow = m_support_params.support_material_flow.with_height(float(base_layer.layer->height));
filler->spacing = m_support_params.support_material_flow.spacing();
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density));
float density = float(support_density);
bool sheath = with_sheath;
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / m_support_params.support_density));
float density = float(m_support_params.support_density);
bool sheath = m_support_params.with_sheath;
bool no_sort = false;
if (base_layer.layer->bottom_z < EPSILON) {
// Base flange (the 1st layer).