diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 4762c3e241..f9d9f5f495 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -680,6 +680,13 @@ Slic3r::Polygons union_(const Slic3r::ExPolygons &subject) { return _clipper(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No); } Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2) { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ApplySafetyOffset::No); } +Slic3r::Polygons union_(Slic3r::Polygons &&subject, const Slic3r::Polygons &subject2) { + if (subject.empty()) + return subject2; + if (subject2.empty()) + return std::move(subject); + return union_(subject, subject2); +} Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2) { return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(subject2), ApplySafetyOffset::No); } diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index d254457099..0baec5c164 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -2011,11 +2011,11 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers( // Find the bottom contact layers above the top surfaces of this layer. static inline SupportGeneratorLayer* detect_bottom_contacts( const SlicingParameters &slicing_params, - const SupportParameters &support_params, + const SupportParameters &support_params, const PrintObject &object, const Layer &layer, // Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height. - const SupportGeneratorLayersPtr &top_contacts, + const SupportGeneratorLayersPtr &top_contacts, // First top contact layer index overlapping with this new bottom interface layer. size_t contact_idx, // To allocate a new layer from. @@ -2888,6 +2888,7 @@ SupportGeneratorLayersPtr generate_raft_base( // If there is brim to be generated, calculate the trimming regions. Polygons brim; if (object.has_brim()) { + // The object does not have a raft. // Calculate the area covered by the brim. const BrimType brim_type = object.config().brim_type; const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner; @@ -2948,12 +2949,20 @@ SupportGeneratorLayersPtr generate_raft_base( if (slicing_params.raft_layers() > 1) { Polygons base; Polygons columns; + Polygons first_layer; if (columns_base != nullptr) { - base = columns_base->polygons; - columns = base; - if (! interface_polygons.empty()) - // Trim the 1st layer columns with the inflated interface polygons. - columns = diff(columns, interface_polygons); + if (columns_base->print_z > slicing_params.raft_contact_top_z - EPSILON) { + // Classic supports with colums above the raft interface. + base = columns_base->polygons; + columns = base; + if (! interface_polygons.empty()) + // Trim the 1st layer columns with the inflated interface polygons. + columns = diff(columns, interface_polygons); + } else { + // Organic supports with raft on print bed. + assert(is_approx(columns_base->print_z, slicing_params.first_print_layer_height)); + first_layer = columns_base->polygons; + } } if (! interface_polygons.empty()) { // Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface. @@ -2967,7 +2976,8 @@ SupportGeneratorLayersPtr generate_raft_base( new_layer.print_z = slicing_params.first_print_layer_height; new_layer.height = slicing_params.first_print_layer_height; new_layer.bottom_z = 0.; - new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(base, inflate_factor_1st_layer) : base; + first_layer = union_(std::move(first_layer), base); + new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(first_layer, inflate_factor_1st_layer) : first_layer; } // Insert the base layers. for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { @@ -3492,12 +3502,7 @@ static inline void fill_expolygons_with_sheath_generate_paths( if (polygons.empty()) return; - if (with_sheath) { - if (density == 0) { - tree_supports_generate_paths(dst, polygons, flow); - return; - } - } else { + if (! with_sheath) { fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow); return; } @@ -4229,7 +4234,7 @@ void generate_support_toolpaths( // Insert the raft base layers. auto n_raft_layers = std::min(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1)); tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), - [&support_layers, &raft_layers, &config, &support_params, &slicing_params, + [&support_layers, &raft_layers, &intermediate_layers, &config, &support_params, &slicing_params, &bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor] (const tbb::blocked_range& range) { for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) @@ -4244,16 +4249,24 @@ void generate_support_toolpaths( filler_interface->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object); + // Print the tree supports cutting through the raft with the exception of the 1st layer, where a full support layer will be printed below + // both the raft and the trees. + // Trim the raft layers with the tree polygons. + const Polygons &tree_polygons = + support_layer_id > 0 && support_layer_id < intermediate_layers.size() && is_approx(intermediate_layers[support_layer_id]->print_z, support_layer.print_z) ? + intermediate_layers[support_layer_id]->polygons : Polygons(); + // Print the support base below the support columns, or the support base for the support columns plus the contacts. if (support_layer_id > 0) { const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ? raft_layer.polygons : //FIXME misusing contact_polygons for support columns. ((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons); + // Trees may cut through the raft layers down to a print bed. + Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter()); + assert(!raft_layer.bridging); if (! to_infill_polygons.empty()) { - assert(! raft_layer.bridging); - Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter()); - Fill * filler = filler_support.get(); + Fill *filler = filler_support.get(); filler->angle = raft_angle_base; filler->spacing = support_params.support_material_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density)); @@ -4261,13 +4274,15 @@ void generate_support_toolpaths( // Destination support_layer.support_fills.entities, // Regions to fill - to_infill_polygons, + tree_polygons.empty() ? to_infill_polygons : diff(to_infill_polygons, tree_polygons), // Filler and its parameters filler, float(support_params.support_density), // Extrusion parameters ExtrusionRole::SupportMaterial, flow, support_params.with_sheath, false); } + if (! tree_polygons.empty()) + tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow); } Fill *filler = filler_interface.get(); @@ -4293,7 +4308,7 @@ void generate_support_toolpaths( // Destination support_layer.support_fills.entities, // Regions to fill - raft_layer.polygons, + tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons), // Filler and its parameters filler, density, // Extrusion parameters @@ -4491,6 +4506,7 @@ void generate_support_toolpaths( float density = float(support_params.support_density); bool sheath = support_params.with_sheath; bool no_sort = false; + bool done = false; if (base_layer.layer->bottom_z < EPSILON) { // Base flange (the 1st layer). filler = filler_first_layer; @@ -4504,18 +4520,21 @@ void generate_support_toolpaths( filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); sheath = true; no_sort = true; + } else if (config.support_material_style == SupportMaterialStyle::smsOrganic) { + tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow); + done = true; } - fill_expolygons_with_sheath_generate_paths( - // Destination - base_layer.extrusions, - // Regions to fill - base_layer.polygons_to_extrude(), - // Filler and its parameters - filler, density, - // Extrusion parameters - ExtrusionRole::SupportMaterial, flow, - sheath, no_sort); - + if (! done) + fill_expolygons_with_sheath_generate_paths( + // Destination + base_layer.extrusions, + // Regions to fill + base_layer.polygons_to_extrude(), + // Filler and its parameters + filler, density, + // Extrusion parameters + ExtrusionRole::SupportMaterial, flow, + sheath, no_sort); } // Merge base_interface_layers to base_layers to avoid unneccessary retractions @@ -4708,3 +4727,4 @@ sub clip_with_shape { */ } // namespace Slic3r + diff --git a/src/libslic3r/TreeSupport.cpp b/src/libslic3r/TreeSupport.cpp index c0938cfe32..60ab764d1f 100644 --- a/src/libslic3r/TreeSupport.cpp +++ b/src/libslic3r/TreeSupport.cpp @@ -116,24 +116,30 @@ TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings& mes if (slicing_params.raft_layers() > 0) { // Fill in raft_layers with the heights of the layers below the first object layer. + // First layer double z = slicing_params.first_print_layer_height; this->raft_layers.emplace_back(z); + // Raft base layers for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) { z += slicing_params.base_raft_layer_height; this->raft_layers.emplace_back(z); } + // Raft interface layers for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) { z += slicing_params.interface_raft_layer_height; this->raft_layers.emplace_back(z); } - assert(is_approx(z, slicing_params.raft_interface_top_z)); - double dist_to_go = slicing_params.object_print_z_min - z; - assert(dist_to_go > slicing_params.min_layer_height - EPSILON); - auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height)); - double step = dist_to_go / nsteps; - for (size_t i = 0; i < nsteps; ++ i) { - z += step; - this->raft_layers.emplace_back(z); + // Raft contact layer + z = slicing_params.raft_contact_top_z; + this->raft_layers.emplace_back(z); + if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) { + // Layers between the raft contacts and bottom of the object. + auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height)); + double step = dist_to_go / nsteps; + for (size_t i = 0; i < nsteps; ++ i) { + z += step; + this->raft_layers.emplace_back(z); + } } } } @@ -401,6 +407,7 @@ void tree_supports_show_error(std::string_view message, bool critical) } }); +#if 0 if (num_raft_layers > 0) { const Layer &first_layer = *print_object.get_layer(0); // Final overhangs. @@ -423,6 +430,7 @@ void tree_supports_show_error(std::string_view message, bool critical) out[num_raft_layers] = std::move(overhangs); throw_on_cancel(); } +#endif return out; } @@ -1053,13 +1061,30 @@ static void generate_initial_areas( std::max(round_up_divide(mesh_config.xy_distance, max_overhang_speed / 2), 2 * mesh_config.z_distance_top_layers) : 0; - //FIXME const size_t num_raft_layers = config.raft_layers.size(); const size_t num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta))); const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1); - std::vector> already_inserted(num_support_layers); + size_t first_tree_layer = 0; + + size_t raft_contact_layer_idx = std::numeric_limits::max(); + if (num_raft_layers > 0 && print_object.layer_count() > 0) { + // Produce raft contact layer outside of the tree support loop, so that no trees will be generated for the raft contact layer. + // Raft layers supporting raft contact interface will be produced by the classic raft generator. + // Find the raft contact layer. + raft_contact_layer_idx = config.raft_layers.size() - 1; + while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON) + -- raft_contact_layer_idx; + // Create the raft contact layer. + SupportGeneratorLayer &raft_contact_layer = layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx); + top_contacts[raft_contact_layer_idx] = &raft_contact_layer; + const ExPolygons &lslices = print_object.get_layer(0)->lslices; + double expansion = print_object.config().raft_expansion.value; + raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled(expansion)) : to_polygons(lslices); + first_tree_layer = print_object.slicing_parameters().raft_layers() - 1; + } std::mutex mutex_layer_storage, mutex_movebounds; + std::vector> already_inserted(num_support_layers); tbb::parallel_for(tbb::blocked_range(first_support_layer, num_support_layers), [&print_object, &volumes, &config, &overhangs, &mesh_config, &mesh_group_settings, &support_params, z_distance_delta, min_xy_dist, force_tip_to_roof, roof_enabled, support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag, @@ -1408,6 +1433,49 @@ static void generate_initial_areas( } } }); + + // Remove tree tips that start below the raft contact, + // remove interface layers below the raft contact. + for (size_t i = 0; i < first_tree_layer; ++i) { + top_contacts[i] = nullptr; + move_bounds[i].clear(); + } + if (raft_contact_layer_idx != std::numeric_limits::max() && print_object.config().raft_expansion.value > 0) { + // If any tips at first_tree_layer now are completely inside the expanded raft layer, remove them as well before they are propagated to the ground. + Polygons &raft_polygons = top_contacts[raft_contact_layer_idx]->polygons; + EdgeGrid::Grid grid(get_extents(raft_polygons).inflated(SCALED_EPSILON)); + grid.create(raft_polygons, Polylines{}, coord_t(scale_(10.))); + SupportElements &first_layer_move_bounds = move_bounds[first_tree_layer]; + double threshold = scaled(print_object.config().raft_expansion.value) * 2.; + first_layer_move_bounds.erase(std::remove_if(first_layer_move_bounds.begin(), first_layer_move_bounds.end(), + [&grid, threshold](const SupportElement &el) { + coordf_t dist; + if (grid.signed_distance_edges(el.state.result_on_layer, threshold, dist)) { + assert(std::abs(dist) < threshold + SCALED_EPSILON); + // Support point is inside the expanded raft, remove it. + return dist < - 0.; + } + return false; + }), first_layer_move_bounds.end()); +#if 0 + // Remove the remaining tips from the raft: Closing operation on tip circles. + if (! first_layer_move_bounds.empty()) { + const double eps = 0.1; + // All tips supporting this layer are expected to have the same radius. + double radius = config.getRadius(first_layer_move_bounds.front().state); + // Connect the tips with the following closing radius. + double closing_distance = radius; + Polygon circle = make_circle(radius + closing_distance, eps); + Polygons circles; + circles.reserve(first_layer_move_bounds.size()); + for (const SupportElement &el : first_layer_move_bounds) { + circles.emplace_back(circle); + circles.back().translate(el.state.result_on_layer); + } + raft_polygons = diff(raft_polygons, offset(union_(circles), - closing_distance)); + } +#endif + } } static unsigned int move_inside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits::max()) @@ -4217,8 +4285,6 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume // Produce the support G-code. // Used by both classic and tree supports. SupportParameters support_params(print_object); - support_params.with_sheath = true; - support_params.support_density = 0; SupportGeneratorLayersPtr interface_layers, base_interface_layers; SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage); #if 1 //#ifdef SLIC3R_DEBUG