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:
parent
2f9ce6bedb
commit
7ff76d0768
19 changed files with 480 additions and 354 deletions
src/libslic3r
|
@ -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).
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue