From fcb714cd241f0847522b9aac57da6b62acdd0152 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 24 Feb 2021 08:48:33 +0100 Subject: [PATCH] Added a single perimeter to the first layer of support or raft. Fixes [Request] Add optional perimeter to raft #756 Fixes First support layer does not stick to bed #2101 New parameters raft_first_layer_density and raft_first_layer_expansion to influence the 1st layer of raft or support. Fixes Allow to disable raft under support structures. #3772 Fixes raft is larger than necessary #2568 Fixes Supports on the build plate should have a solid bottom interface for better adhesion #1165 Changed the 1st layer infill to rectilinear even for soluble materials. Fixes first layer of support for multi filament support oddly spaced #1445 Fixes Full Soluble Materials interfacing into Models + Soluble material noise on Bed #684 --- src/libslic3r/ExtrusionEntity.hpp | 14 ++ src/libslic3r/ExtrusionEntityCollection.hpp | 5 + src/libslic3r/GCode.cpp | 12 +- src/libslic3r/Preset.cpp | 3 +- src/libslic3r/Print.cpp | 3 +- src/libslic3r/PrintConfig.cpp | 19 ++ src/libslic3r/PrintConfig.hpp | 4 + src/libslic3r/PrintObject.cpp | 2 + src/libslic3r/SupportMaterial.cpp | 194 ++++++++++++-------- src/slic3r/GUI/Tab.cpp | 2 + 10 files changed, 178 insertions(+), 80 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 9b972f211..7e9e9eb54 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -102,6 +102,7 @@ public: virtual double min_mm3_per_mm() const = 0; virtual Polyline as_polyline() const = 0; virtual void collect_polylines(Polylines &dst) const = 0; + virtual void collect_points(Points &dst) const = 0; virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; } virtual double length() const = 0; virtual double total_volume() const = 0; @@ -167,6 +168,7 @@ public: double min_mm3_per_mm() const override { return this->mm3_per_mm; } Polyline as_polyline() const override { return this->polyline; } void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } + void collect_points(Points &dst) const override { append(dst, this->polyline.points); } double total_volume() const override { return mm3_per_mm * unscale(length()); } private: @@ -217,6 +219,12 @@ public: double min_mm3_per_mm() const override; Polyline as_polyline() const override; void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); } + void collect_points(Points &dst) const override { + size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); }); + dst.reserve(dst.size() + n); + for (const ExtrusionPath &p : this->paths) + append(dst, p.polyline.points); + } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } }; @@ -268,6 +276,12 @@ public: double min_mm3_per_mm() const override; Polyline as_polyline() const override { return this->polygon().split_at_first_point(); } void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); } + void collect_points(Points &dst) const override { + size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); }); + dst.reserve(dst.size() + n); + for (const ExtrusionPath &p : this->paths) + append(dst, p.polyline.points); + } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } //static inline std::string role_to_string(ExtrusionLoopRole role); diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 5e40ab32e..6e62a45fd 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -117,6 +117,11 @@ public: extrusion_entity->collect_polylines(dst); } + void collect_points(Points &dst) const override { + for (ExtrusionEntity* extrusion_entity : this->entities) + extrusion_entity->collect_points(dst); + } + double length() const override { throw Slic3r::RuntimeError("Calling length() on a ExtrusionEntityCollection"); return 0.; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index dadd878cf..f3bffe331 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2573,10 +2573,11 @@ std::string GCode::extrude_infill(const Print &print, const std::vectorextrude_path(*path, label, speed); else { const ExtrusionMultiPath *multipath = dynamic_cast(ee); - assert(multipath != nullptr); if (multipath) gcode += this->extrude_multi_path(*multipath, label, speed); + else { + const ExtrusionEntityCollection *eec = dynamic_cast(ee); + assert(eec); + if (eec) + gcode += this->extrude_support(*eec); + } } } } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 69b13376c..6627e5f72 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -426,7 +426,8 @@ const std::vector& Preset::print_options() "bridge_speed", "gap_fill_speed", "gap_fill_enabled", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield", "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", - "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", + "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", + "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index b79e589c3..643cc9a97 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1731,8 +1731,7 @@ void Print::_make_skirt() for (const SupportLayer *layer : object->support_layers()) { if (layer->print_z > skirt_height_z) break; - for (const ExtrusionEntity *extrusion_entity : layer->support_fills.entities) - append(object_points, extrusion_entity->as_polyline().points); + layer->support_fills.collect_points(object_points); } // Repeat points for each object copy. for (const PrintInstance &instance : object->instances()) { diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index b494f7333..e7e8240c7 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1775,6 +1775,25 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionString("")); def->cli = ConfigOptionDef::nocli; + def = this->add("raft_first_layer_density", coPercent); + def->label = L("First layer density"); + def->category = L("Support material"); + def->tooltip = L("Density of the first raft or support layer."); + def->sidetext = L("%"); + def->min = 0; + def->max = 150; + def->mode = comExpert; + def->set_default_value(new ConfigOptionPercent(90)); + + def = this->add("raft_first_layer_expansion", coFloat); + def->label = L("First layer expansion"); + def->category = L("Support material"); + def->tooltip = L("Expansion of the first raft or support layer to improve adhesion to print bed."); + def->sidetext = L("mm"); + def->min = 0; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(3.)); + def = this->add("raft_layers", coInt); def->label = L("Raft layers"); def->category = L("Support material"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index c205180d0..af50388c7 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -473,6 +473,8 @@ public: // Force the generation of solid shells between adjacent materials/volumes. ConfigOptionBool interface_shells; ConfigOptionFloat layer_height; + ConfigOptionPercent raft_first_layer_density; + ConfigOptionFloat raft_first_layer_expansion; ConfigOptionInt raft_layers; ConfigOptionEnum seam_position; // ConfigOptionFloat seam_preferred_direction; @@ -520,6 +522,8 @@ protected: OPT_PTR(infill_only_where_needed); OPT_PTR(interface_shells); OPT_PTR(layer_height); + OPT_PTR(raft_first_layer_density); + OPT_PTR(raft_first_layer_expansion); OPT_PTR(raft_layers); OPT_PTR(seam_position); OPT_PTR(slice_closing_radius); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index b8e019943..ed306f674 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -576,6 +576,8 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_material_synchronize_layers" || opt_key == "support_material_threshold" || opt_key == "support_material_with_sheath" + || opt_key == "raft_first_layer_density" + || opt_key == "raft_first_layer_expansion" || opt_key == "dont_support_bridges" || opt_key == "first_layer_extrusion_width") { steps.emplace_back(posSupportMaterial); diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index a7cf4d323..c95c5c0a2 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -2536,7 +2536,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf // How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed. const float inflate_factor_fine = float(scale_((m_slicing_params.raft_layers() > 1) ? 0.5 : EPSILON)); - const float inflate_factor_1st_layer = float(scale_(3.)) - inflate_factor_fine; + const float inflate_factor_1st_layer = std::max(0.f, float(scale_(object.config().raft_first_layer_expansion)) - inflate_factor_fine); MyLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front(); MyLayer *interfaces = interface_layers.empty() ? nullptr : interface_layers.front(); MyLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front(); @@ -2581,7 +2581,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 = offset(base, inflate_factor_1st_layer); + new_layer.polygons = inflate_factor_1st_layer > 0 ? offset(base, inflate_factor_1st_layer) : base; } // Insert the base layers. for (size_t i = 1; i < m_slicing_params.base_raft_layers; ++ i) { @@ -2608,7 +2608,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf } else if (columns_base != nullptr) { // Expand the bases of the support columns in the 1st layer. columns_base->polygons = diff( - offset(columns_base->polygons, inflate_factor_1st_layer), + inflate_factor_1st_layer > 0 ? offset(columns_base->polygons, inflate_factor_1st_layer) : columns_base->polygons, offset(m_object->layers().front()->lslices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (contacts != nullptr) columns_base->polygons = diff(columns_base->polygons, interface_polygons); @@ -2754,6 +2754,41 @@ std::pairfill_surface(&surface, fill_params); + } catch (InfillFailedException &) { + } + extrusion_entities_append_paths( + dst, + std::move(polylines), + role, + flow.mm3_per_mm(), flow.width, flow.height); +} + +static inline void fill_expolygons_generate_paths( + ExtrusionEntitiesPtr &dst, + ExPolygons &&expolygons, + Fill *filler, + const FillParams &fill_params, + float density, + ExtrusionRole role, + const Flow &flow) +{ + for (ExPolygon &expoly : expolygons) + fill_expolygon_generate_paths(dst, std::move(expoly), filler, fill_params, density, role, flow); +} + static inline void fill_expolygons_generate_paths( ExtrusionEntitiesPtr &dst, ExPolygons &&expolygons, @@ -2763,20 +2798,53 @@ static inline void fill_expolygons_generate_paths( const Flow &flow) { FillParams fill_params; - fill_params.density = density; + fill_params.density = density; fill_params.dont_adjust = true; - for (ExPolygon &expoly : expolygons) { - Surface surface(stInternal, std::move(expoly)); + fill_expolygons_generate_paths(dst, std::move(expolygons), filler, fill_params, density, role, flow); +} + +static inline void fill_expolygons_with_sheath_generate_paths( + ExtrusionEntitiesPtr &dst, + const Polygons &polygons, + Fill *filler, + float density, + ExtrusionRole role, + const Flow &flow, + bool with_sheath) +{ + if (polygons.empty()) + return; + + if (! with_sheath) { + fill_expolygons_generate_paths(dst, offset2_ex(polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON)), filler, density, role, flow); + return; + } + + FillParams fill_params; + fill_params.density = density; + fill_params.dont_adjust = true; + + double spacing = flow.scaled_spacing(); + // 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()))) { + // Don't reorder the skirt and its infills. + auto eec = std::make_unique(); + eec->no_sort = true; + // Draw the perimeters. Polylines polylines; - try { - polylines = filler->fill_surface(&surface, fill_params); - } catch (InfillFailedException &) { - } - extrusion_entities_append_paths( - dst, - std::move(polylines), - role, - flow.mm3_per_mm(), flow.width, flow.height); + polylines.reserve(expoly.holes.size() + 1); + for (size_t i = 0; i <= expoly.holes.size(); ++ i) { + Polyline pl(i == 0 ? expoly.contour.points : expoly.holes[i - 1].points); + pl.points.emplace_back(pl.points.front()); + pl.clip_end(clip_length); + polylines.emplace_back(std::move(pl)); + } + extrusion_entities_append_paths(eec->entities, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); + // Fill in the rest. + fill_expolygons_generate_paths(eec->entities, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow); + dst.emplace_back(eec.release()); } } @@ -3430,45 +3498,26 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Print the support base below the support columns, or the support base for the support columns plus the contacts. if (support_layer_id > 0) { - Polygons to_infill_polygons = (support_layer_id < m_slicing_params.base_raft_layers) ? + const Polygons &to_infill_polygons = (support_layer_id < m_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); if (! to_infill_polygons.empty()) { Flow flow(float(m_support_material_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging); - // find centerline of the external loop/extrusions - ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? - // union_ex(base_polygons, true) : - offset2_ex(to_infill_polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON)) : - offset2_ex(to_infill_polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON - 0.5*flow.scaled_width())); - if (! to_infill.empty() && with_sheath) { - // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. - // TODO: use brim ordering algorithm - to_infill_polygons = to_polygons(to_infill); - // TODO: use offset2_ex() - to_infill = offset_ex(to_infill, float(- 0.4 * flow.scaled_spacing())); - extrusion_entities_append_paths( - support_layer.support_fills.entities, - to_polylines(std::move(to_infill_polygons)), - erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); - } - if (! to_infill.empty()) { - // We don't use $base_flow->spacing because we need a constant spacing - // value that guarantees that all layers are correctly aligned. - Fill *filler = filler_support.get(); - filler->angle = raft_angle_base; - filler->spacing = m_support_material_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density)); - fill_expolygons_generate_paths( - // Destination - support_layer.support_fills.entities, - // Regions to fill - std::move(to_infill), - // Filler and its parameters - filler, float(support_density), - // Extrusion parameters - erSupportMaterial, flow); - } + Fill * filler = filler_support.get(); + filler->angle = raft_angle_base; + filler->spacing = m_support_material_flow.spacing(); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / 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), + // Extrusion parameters + erSupportMaterial, flow, + with_sheath); } } @@ -3526,12 +3575,21 @@ void PrintObjectSupportMaterial::generate_toolpaths( size_t idx_layer_bottom_contact = size_t(-1); size_t idx_layer_top_contact = size_t(-1); size_t idx_layer_intermediate = size_t(-1); - size_t idx_layer_interface = size_t(-1); - size_t idx_layer_base_interface = size_t(-1); - auto filler_interface = std::unique_ptr(Fill::new_from_type(m_slicing_params.soluble_interface ? ipConcentric : ipRectilinear)); + size_t idx_layer_interface = size_t(-1); + size_t idx_layer_base_interface = size_t(-1); + const auto fill_type_interface = m_slicing_params.soluble_interface ? ipConcentric : ipRectilinear; + const auto fill_type_first_layer = ipRectilinear; + auto filler_interface = std::unique_ptr(Fill::new_from_type(fill_type_interface)); + // Filler for the 1st layer interface, if different from filler_interface. + auto filler_first_layer_ptr = std::unique_ptr(range.begin() == 0 && fill_type_interface != 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(base_interface_layers.empty() ? nullptr : Fill::new_from_type(ipRectilinear)); auto filler_support = std::unique_ptr(Fill::new_from_type(infill_pattern)); filler_interface->set_bounding_box(bbox_object); + if (filler_first_layer_ptr) + filler_first_layer_ptr->set_bounding_box(bbox_object); if (filler_base_interface) filler_base_interface->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object); @@ -3656,7 +3714,6 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Base support or flange. if (! base_layer.empty() && ! base_layer.polygons_to_extrude().empty()) { - //FIXME When paralellizing, each thread shall have its own copy of the fillers. Fill *filler = filler_support.get(); filler->angle = angles[support_layer_id % angles.size()]; // We don't use $base_flow->spacing because we need a constant spacing @@ -3669,42 +3726,31 @@ void PrintObjectSupportMaterial::generate_toolpaths( 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); - // find centerline of the external loop/extrusions - ExPolygons to_infill = (support_layer_id == 0 || ! with_sheath) ? - // union_ex(base_polygons, true) : - offset2_ex(base_layer.polygons_to_extrude(), float(SCALED_EPSILON), float(- SCALED_EPSILON)) : - offset2_ex(base_layer.polygons_to_extrude(), float(SCALED_EPSILON), float(- SCALED_EPSILON - 0.5*flow.scaled_width())); + bool sheath = with_sheath; if (base_layer.layer->bottom_z < EPSILON) { // Base flange (the 1st layer). - filler = filler_interface.get(); + filler = filler_first_layer; filler->angle = Geometry::deg2rad(float(m_object_config->support_material_angle.value + 90.)); - density = 0.5f; + density = float(m_object_config->raft_first_layer_density.value * 0.01); flow = m_first_layer_flow; // use the proper spacing for first layer as we don't need to align // its pattern to the other layers //FIXME When paralellizing, each thread shall have its own copy of the fillers. filler->spacing = flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); - } else if (with_sheath) { - // Draw a perimeter all around the support infill. This makes the support stable, but difficult to remove. - // TODO: use brim ordering algorithm - Polygons to_infill_polygons = to_polygons(to_infill); - // TODO: use offset2_ex() - to_infill = offset_ex(to_infill, - 0.4f * float(flow.scaled_spacing())); - extrusion_entities_append_paths( - base_layer.extrusions, - to_polylines(std::move(to_infill_polygons)), - erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height); + sheath = true; } - fill_expolygons_generate_paths( + fill_expolygons_with_sheath_generate_paths( // Destination - base_layer.extrusions, + base_layer.extrusions, // Regions to fill - std::move(to_infill), + base_layer.polygons_to_extrude(), // Filler and its parameters filler, density, // Extrusion parameters - erSupportMaterial, flow); + erSupportMaterial, flow, + sheath); + } // Merge base_interface_layers to base_layers to avoid unneccessary retractions diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 686528a1c..e6f04926c 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1499,6 +1499,8 @@ void TabPrint::build() optgroup = page->new_optgroup(L("Raft")); optgroup->append_single_option_line("raft_layers", category_path + "raft-layers"); + optgroup->append_single_option_line("raft_first_layer_density", category_path + "raft-first-layer-density"); + optgroup->append_single_option_line("raft_first_layer_expansion", category_path + "raft-first-layer-expansion"); // # optgroup->append_single_option_line(get_option_("raft_contact_distance"); optgroup = page->new_optgroup(L("Options for support material and raft"));