diff --git a/resources/ui_layout/default/extruder.ui b/resources/ui_layout/default/extruder.ui index 7e2939f8436..ddbcb8e3ec8 100644 --- a/resources/ui_layout/default/extruder.ui +++ b/resources/ui_layout/default/extruder.ui @@ -41,7 +41,10 @@ group:General wipe setting:idx:label$Depth:wipe_inside_depth end_line setting:idx:wipe_extra_perimeter - setting:idx:sidetext_width$5:seam_gap + line:Seam gap + setting:idx:label$_:sidetext_width$5:seam_gap + setting:idx:label$for external perimeters:sidetext_width$5:seam_gap_external + end_line group:Retraction when tool is disabled (advanced settings for multi-extruder setups) setting:label$Minimum retraction:idx:retract_length_toolchange setting:label$Extra unretraction:idx:retract_restart_extra_toolchange diff --git a/resources/ui_layout/example/extruder.ui b/resources/ui_layout/example/extruder.ui index 5c94f3d02bf..ddbcb8e3ec8 100644 --- a/resources/ui_layout/example/extruder.ui +++ b/resources/ui_layout/example/extruder.ui @@ -41,7 +41,10 @@ group:General wipe setting:idx:label$Depth:wipe_inside_depth end_line setting:idx:wipe_extra_perimeter - setting:idx:seam_gap + line:Seam gap + setting:idx:label$_:sidetext_width$5:seam_gap + setting:idx:label$for external perimeters:sidetext_width$5:seam_gap_external + end_line group:Retraction when tool is disabled (advanced settings for multi-extruder setups) setting:label$Minimum retraction:idx:retract_length_toolchange setting:label$Extra unretraction:idx:retract_restart_extra_toolchange diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 1c545881ee2..a104a9fa136 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -338,8 +338,8 @@ class ExtrusionMultiEntity : public ExtrusionEntity { bool is_loop() const override { return false; } ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } - virtual const Point& first_point() const override { return this->paths.front().as_polyline().front(); } - virtual const Point& last_point() const override { return this->paths.back().as_polyline().back(); } + virtual const Point& first_point() const override { return this->paths.front().polyline.as_polyline().front(); } + virtual const Point& last_point() const override { return this->paths.back().polyline.as_polyline().back(); } virtual void reverse() override { for (THING &entity : this->paths) diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 3b6d7cf2870..af18548a115 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -19,14 +19,15 @@ void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, } ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths) - : no_sort(false) + : m_no_sort(false), m_no_reverse(false) { this->append(paths); } ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const ExtrusionEntityCollection &other) { - this->no_sort = other.no_sort; + this->m_no_sort = other.m_no_sort; + this->m_no_reverse = other.m_no_reverse; clear(); this->append(other.m_entities); return *this; @@ -35,7 +36,8 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const Extrusion void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c) { std::swap(this->m_entities, c.m_entities); - std::swap(this->no_sort, c.no_sort); + std::swap(this->m_no_sort, c.m_no_sort); + std::swap(this->m_no_reverse, c.m_no_reverse); } void ExtrusionEntityCollection::clear() diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index b6b6f8c3eb4..09d64160234 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -26,9 +26,9 @@ class ExtrusionEntityCollection : public ExtrusionEntity { private: // set to tru to forbit to reorder and reverse all entities indie us. - bool no_sort; + bool m_no_sort; // even if no_sort, allow to reverse() us (and our entities if they allow it, but they should) - bool no_reverse; + bool m_no_reverse; ExtrusionEntitiesPtr m_entities; // we own these entities public: virtual ExtrusionEntityCollection* clone() const override { return new ExtrusionEntityCollection(*this); } @@ -40,16 +40,17 @@ class ExtrusionEntityCollection : public ExtrusionEntity /// Iterating over this needs to check each child to see if it, too is a collection. const ExtrusionEntitiesPtr& entities() const { return m_entities; } ExtrusionEntitiesPtr& set_entities() { return m_entities; } - ExtrusionEntityCollection(): no_sort(false), no_reverse(false) {} - ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort), no_reverse(other.no_reverse) { this->append(other.entities()); } - ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : m_entities(std::move(other.m_entities)), no_sort(other.no_sort), no_reverse(other.no_reverse) {} + ExtrusionEntityCollection() : m_no_sort(false), m_no_reverse(false) {} + ExtrusionEntityCollection(bool no_sort, bool no_reverse) : m_no_sort(no_sort), m_no_reverse(no_reverse) {} + ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : m_no_sort(other.m_no_sort), m_no_reverse(other.m_no_reverse) { this->append(other.entities()); } + ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : m_entities(std::move(other.m_entities)), m_no_sort(other.m_no_sort), m_no_reverse(other.m_no_reverse) {} explicit ExtrusionEntityCollection(const ExtrusionPaths &paths); ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other); ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) { this->clear(); this->m_entities = std::move(other.m_entities); - this->no_sort = other.no_sort; - this->no_reverse = other.no_reverse; + this->m_no_sort = other.m_no_sort; + this->m_no_reverse = other.m_no_reverse; return *this; } ~ExtrusionEntityCollection() override { clear(); } @@ -66,9 +67,9 @@ class ExtrusionEntityCollection : public ExtrusionEntity } return out; } - void set_can_sort_reverse(bool sort, bool reverse) { this->no_sort = !sort; this->no_reverse = !reverse; } - bool can_sort() const { return !this->no_sort; } - bool can_reverse() const override { return can_sort() || !this->no_reverse; } + void set_can_sort_reverse(bool sort, bool reverse) { this->m_no_sort = !sort; this->m_no_reverse = !reverse; } + bool can_sort() const { return !this->m_no_sort; } + bool can_reverse() const override { return can_sort() || !this->m_no_reverse; } bool empty() const { return this->m_entities.empty(); } void clear(); void swap (ExtrusionEntityCollection &c); @@ -102,7 +103,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed); ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erNone) const { if (role == erNone) role = this->role(); - if( this->no_sort || (role == erMixed) ) + if( this->m_no_sort || (role == erMixed) ) return *this; else return chained_path_from(this->m_entities, start_near, role); diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 26c57c071ff..18ea9fdfd08 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -73,6 +73,20 @@ FillConcentric::_fill_surface_single( // We want the loops to be split inside the G-code generator to get optimum path planning. } +void append_loop_into_collection(ExtrusionEntityCollection& storage, ExtrusionRole& good_role, const FillParams& params, Polygon& polygon) { + double flow = params.flow.mm3_per_mm() * params.flow_mult; + double width = params.flow.width() * params.flow_mult; + double height = params.flow.height(); + if (polygon.is_valid()) { + //default to ccw + polygon.make_counter_clockwise(); + ExtrusionPath path(good_role, flow, width, height); + path.polyline.append(std::move(polygon.points)); + path.polyline.append(path.polyline.front()); + storage.append(ExtrusionLoop{ std::move(path) }); + } +} + void FillConcentricWGapFill::fill_surface_extrusion( const Surface *surface, @@ -105,21 +119,34 @@ FillConcentricWGapFill::fill_surface_extrusion( } ExPolygons gaps; - Polygons loops = to_polygons(expolygon); + std::vector< std::vector> bunch_2_shell_2_loops; + bunch_2_shell_2_loops.emplace_back(); // create a new bunch before a gap + bunch_2_shell_2_loops.back().push_back(to_polygons(expolygon)); // add first shell + std::vector bunch_2_gaps; // size = bunch_2_shell_2_loops.size() (-1) ExPolygons last = { expolygon }; bool first = true; while (!last.empty()) { ExPolygons next_onion = offset2_ex(last, -(distance + scale_d(this->get_spacing()) / 2), +(scale_d(this->get_spacing()) / 2)); - append(loops, to_polygons(next_onion)); - append(gaps, diff_ex( + ExPolygons new_gaps = diff_ex( offset_ex(last, -0.5f * distance), - offset_ex(next_onion, 0.5f * distance + 10))); // safety offset + offset_ex(next_onion, 0.5f * distance + 10)); // 10 = safety offset + bunch_2_shell_2_loops.back().push_back(to_polygons(next_onion)); + if (!new_gaps.empty()) { + append(gaps, new_gaps); + bunch_2_gaps.push_back(std::move(new_gaps)); + //create a new collection for next bunch. + bunch_2_shell_2_loops.emplace_back(); + } last = next_onion; if (first && !this->no_overlap_expolygons.empty()) { gaps = intersection_ex(gaps, this->no_overlap_expolygons); } first = false; } + if (bunch_2_shell_2_loops.back().empty()) { + assert(bunch_2_shell_2_loops.size() > bunch_2_gaps.size()); + bunch_2_shell_2_loops.pop_back(); + } // generate paths from the outermost to the innermost, to avoid // adhesion problems of the first central tiny loops @@ -130,73 +157,207 @@ FillConcentricWGapFill::fill_surface_extrusion( //get the role ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); - ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection(); - coll_nosort->set_can_sort_reverse(false, false); //can be sorted inside the pass - extrusion_entities_append_loops( - coll_nosort->set_entities(), loops, - good_role, - params.flow.mm3_per_mm() * params.flow_mult, - params.flow.width() * params.flow_mult, - params.flow.height()); - - //add gapfills - if (!gaps.empty() && params.density >= 1) { - // get parameters - coordf_t min = 0.2 * distance * (1 - INSET_OVERLAP_TOLERANCE); - //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). - min = std::max(min, double(Flow::new_from_spacing((float)EPSILON, (float)params.flow.nozzle_diameter(), (float)params.flow.height(), (float)params.flow.spacing_ratio(), false).scaled_width())); - coordf_t real_max = 2.5 * distance; - const coordf_t minwidth = scale_d(params.config->get_abs_value("gap_fill_min_width", params.flow.width())); - const coordf_t maxwidth = scale_d(params.config->get_abs_value("gap_fill_max_width", params.flow.width())); - const coord_t minlength = scale_t(params.config->get_abs_value("gap_fill_min_length", params.flow.width())); - if (minwidth > 0) { - min = std::max(min, minwidth); - } - coordf_t max = real_max; - if (maxwidth > 0) { - max = std::min(max, maxwidth); - } - const coord_t gapfill_extension = scale_t(params.config->get_abs_value("gap_fill_extension", params.flow.width())); - - // collapse - ExPolygons gaps_ex = diff_ex( - offset2_ex(gaps, -min / 2, +min / 2), - offset2_ex(gaps, -max / 2, +max / 2), - ApplySafetyOffset::Yes); - ThickPolylines polylines; - for (const ExPolygon &ex : gaps_ex) { - //remove too small gaps that are too hard to fill. - //ie one that are smaller than an extrusion with width of min and a length of max. - if (ex.area() > min_gapfill_area) { - Geometry::MedialAxis md{ ex, coord_t(real_max), coord_t(min), scale_t(params.flow.height()) }; - if (minlength > 0) { - md.set_min_length(minlength); + ExtrusionEntityCollection* root_collection_nosort = new ExtrusionEntityCollection(true, true); + + //pattern (don't modify/move it) + const ExtrusionEntityCollection eec_pattern_no_sort{ true, true }; + + assert(bunch_2_shell_2_loops.size() == bunch_2_gaps.size() || bunch_2_shell_2_loops.size() == bunch_2_gaps.size() + 1); + //for each "shell" (loop to print before a gap) + for (int idx_bunch = 0; idx_bunch < bunch_2_shell_2_loops.size(); idx_bunch++) { + + //we have some "starting loops". for each 'shell', we get each loop and find (by searching which one it fit inside) its island. + // if there is none or multiple, then we have to start again from these new loops. + std::vector& shells = bunch_2_shell_2_loops[idx_bunch]; + assert(!shells.empty()); + + // leafs where you can add a loop (only one per shell, or you need to split it) + std::vector leafs; + + //initialisation with first shell + root_collection_nosort->append(ExtrusionEntityCollection{}); + ExtrusionEntityCollection* root_sortable = static_cast(root_collection_nosort->set_entities().back()); + + struct Leaf { + size_t count; + ExtrusionEntityCollection* sortable; + }; + // for each shell of this bunch + + for (size_t idx_shell = 0; idx_shell < shells.size(); ++idx_shell) { + Polygons& islands = shells[idx_shell]; + std::vector nb_childs(leafs.size(), Leaf{ 0, nullptr }); + assert(nb_childs.size() == leafs.size()); + // for each island + for (size_t idx_island = 0; idx_island < islands.size(); ++idx_island) { + //find a leafs to append it. + size_t added_idx = size_t(-1); + if (islands[idx_island].is_clockwise()) { + //hole -> it's the leafs that should be inside me + islands[idx_island].make_counter_clockwise(); + for (size_t idx_child = 0; idx_child < nb_childs.size(); ++idx_child) { + assert(!leafs[idx_child]->entities().empty()); + assert(leafs[idx_child]->entities().back()->is_loop()); + if (islands[idx_island].contains(leafs[idx_child]->entities().back()->first_point())) { + added_idx = idx_child; + break; + } + } + } else { + for (size_t idx_child = 0; idx_child < nb_childs.size(); ++idx_child) { + assert(!leafs[idx_child]->entities().empty()); + assert(leafs[idx_child]->entities().back()->is_loop()); + if (leafs[idx_child]->entities().back()->is_loop() && + static_cast(leafs[idx_child]->entities().back())->polygon().contains(islands[idx_island].first_point())) { + added_idx = idx_child; + break; + } + } + } + if (added_idx != size_t(-1)) { + Leaf& leaf_count = nb_childs[added_idx]; + ExtrusionEntityCollection* leaf_coll = leafs[added_idx]; + //check if it's taken + if (leaf_count.count == 0) { + append_loop_into_collection(*leaf_coll, good_role, params, islands[idx_island]); + } else if (leaf_count.count == 1) { + //remove last entity (from the count==0) to put it into a new collection + ExtrusionEntity* elt = leaf_coll->set_entities().back(); + leaf_coll->set_entities().pop_back(); + //add sortbale collection inside + leaf_coll->append(ExtrusionEntityCollection{});; + leaf_count.sortable = static_cast(leaf_coll->set_entities().back()); + ExtrusionEntityCollection new_coll_nosort{ true, true }; + new_coll_nosort.set_entities().push_back(elt); + leaf_count.sortable->append(std::move(new_coll_nosort)); + } + if (leaf_count.sortable) { + //add new collection + ExtrusionEntityCollection new_coll_nosort{ true, true }; + append_loop_into_collection(new_coll_nosort, good_role, params, islands[idx_island]); + leaf_count.sortable->append(std::move(new_coll_nosort)); + } + ++leaf_count.count; + + } else { + //create new root (should only happen on the first shell 'initialisation') + root_sortable->append(eec_pattern_no_sort); + leafs.push_back(static_cast(root_sortable->set_entities().back())); + append_loop_into_collection(*leafs.back(), good_role, params, islands[idx_island]); } - if (gapfill_extension > 0) { - md.set_extension_length(gapfill_extension); + } + //remove old leafs + assert(leafs.size() >= nb_childs.size()); + for (size_t idx_child = nb_childs.size() - 1; idx_child < nb_childs.size(); ++idx_child) { + if (nb_childs[idx_child].count > 1) { + leafs.erase(leafs.begin() + idx_child); } - md.set_biggest_width(max); - md.build(polylines); } } - if (!polylines.empty() && !is_bridge(good_role)) { - ExtrusionEntitiesPtr gap_fill_entities = Geometry::thin_variable_width(polylines, erGapFill, params.flow, scale_t(params.config->get_computed_value("resolution_internal"))); - if (!gap_fill_entities.empty()) { - //set role if needed - if (good_role != erSolidInfill) { - ExtrusionSetRole set_good_role(good_role); - for (ExtrusionEntity* ptr : gap_fill_entities) - ptr->visit(set_good_role); + //TODO: move items that are alone in a collection to the upper collection. + + //add gapfills + if (idx_bunch < bunch_2_gaps.size() && !bunch_2_gaps[idx_bunch].empty() && params.density >= 1) { + // get parameters + coordf_t min = 0.2 * distance * (1 - INSET_OVERLAP_TOLERANCE); + //be sure we don't gapfill where the perimeters are already touching each other (negative spacing). + min = std::max(min, double(Flow::new_from_spacing((float)EPSILON, (float)params.flow.nozzle_diameter(), (float)params.flow.height(), (float)params.flow.spacing_ratio(), false).scaled_width())); + coordf_t real_max = 2.5 * distance; + const coordf_t minwidth = scale_d(params.config->get_abs_value("gap_fill_min_width", params.flow.width())); + const coordf_t maxwidth = scale_d(params.config->get_abs_value("gap_fill_max_width", params.flow.width())); + const coord_t minlength = scale_t(params.config->get_abs_value("gap_fill_min_length", params.flow.width())); + if (minwidth > 0) { + min = std::max(min, minwidth); + } + coordf_t max = real_max; + if (maxwidth > 0) { + max = std::min(max, maxwidth); + } + const coord_t gapfill_extension = scale_t(params.config->get_abs_value("gap_fill_extension", params.flow.width())); + + // collapse + ExPolygons gaps_ex = diff_ex( + offset2_ex(bunch_2_gaps[idx_bunch], -min / 2, +min / 2), + offset2_ex(bunch_2_gaps[idx_bunch], -max / 2, +max / 2), + ApplySafetyOffset::Yes); + ThickPolylines polylines; + for (const ExPolygon& ex : gaps_ex) { + //remove too small gaps that are too hard to fill. + //ie one that are smaller than an extrusion with width of min and a length of max. + if (ex.area() > min_gapfill_area) { + Geometry::MedialAxis md{ ex, coord_t(real_max), coord_t(min), scale_t(params.flow.height()) }; + if (minlength > 0) { + md.set_min_length(minlength); + } + if (gapfill_extension > 0) { + md.set_extension_length(gapfill_extension); + } + md.set_biggest_width(max); + md.build(polylines); + } + } + //search if we can add some at the end of a leaf + for (size_t idx_polyline = 0; idx_polyline < polylines.size(); ++idx_polyline) { + ThickPolyline& poly = polylines[idx_polyline]; + assert(!poly.empty()); + for (size_t idx_leaf = 0; idx_leaf < leafs.size(); ++idx_leaf) { + assert(!leafs[idx_leaf]->entities().empty()); + const ExtrusionEntitiesPtr& leaf_entities = leafs[idx_leaf]->entities(); + //get last loop + size_t idx_last_loop = leaf_entities.size() - 1; + while (!leaf_entities[idx_last_loop]->is_loop()) { + if (idx_last_loop == 0) { + assert(false); + //goto goto_next_polyline; + } + --idx_last_loop; + } + //test + assert(leafs[idx_leaf]->entities()[idx_last_loop]->is_loop()); + if (leafs[idx_leaf]->entities()[idx_last_loop]->is_loop() && + static_cast(leafs[idx_leaf]->entities()[idx_last_loop])->polygon().contains(poly.points[poly.size() / 2])) { + //do gapfill locally + leafs[idx_leaf]->append( + Geometry::variable_width( + poly, erGapFill, + params.flow, + scale_t(params.config->get_computed_value("resolution_internal")), + params.flow.scaled_width() / 10) + ); + polylines.erase(polylines.begin() + idx_polyline); + --idx_polyline; + break; + } + } + //goto_next_polyline: + } + if (!polylines.empty() && !is_bridge(good_role)) { + ExtrusionEntitiesPtr gap_fill_entities = Geometry::thin_variable_width(polylines, erGapFill, params.flow, scale_t(params.config->get_computed_value("resolution_internal"))); + if (!gap_fill_entities.empty()) { + //set role if needed + if (good_role != erSolidInfill) { + ExtrusionSetRole set_good_role(good_role); + for (ExtrusionEntity* ptr : gap_fill_entities) + ptr->visit(set_good_role); + } + + //move them into the collection + if (gap_fill_entities.size() == 1) { + root_collection_nosort->append(std::move(gap_fill_entities)); + } else { + ExtrusionEntityCollection gapsCollection; + gapsCollection.append(std::move(gap_fill_entities)); + root_collection_nosort->append(std::move(gapsCollection)); + } } - //move them into the collection - coll_nosort->append(std::move(gap_fill_entities)); } } } - if (!coll_nosort->entities().empty()) - out.push_back(coll_nosort); - else delete coll_nosort; + + if (!root_collection_nosort->entities().empty()) + out.push_back(root_collection_nosort); + else delete root_collection_nosort; } // external gapfill diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 68ec2ce4eb8..da9ade89db4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3556,6 +3556,12 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s ExtrusionPaths &paths = loop_to_seam.paths; if (false && m_enable_loop_clipping && m_writer.tool_is_extruder()) { coordf_t clip_length = scale_(m_config.seam_gap.get_abs_value(m_writer.tool()->id(), EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + if (original_loop.role() == erExternalPerimeter) { + coordf_t clip_length_external = scale_(m_config.seam_gap_external.get_abs_value(m_writer.tool()->id(), unscaled(clip_length))); + if (clip_length_external > 0) { + clip_length = clip_length_external; + } + } coordf_t min_clip_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)) * 0.15; // get paths @@ -3978,7 +3984,7 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, //abord if the two vec are too different double prod_scal = vec_start.dot(vec_end); if (prod_scal < 0.2) { - std::cout << "notch abord: too different sides\n"; + BOOST_LOG_TRIVIAL(warning) << "notch abord: too different sides\n"; return; } //use a vec that is the mean between the two. @@ -3996,41 +4002,21 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, } else { check_angle = end_point.ccw_angle(prev_point, start_point); if ((is_hole_loop ? -check_angle : check_angle) > this->m_config.seam_notch_angle.value * PI / 180.) { - std::cout << "notch abord: too big angle\n"; + BOOST_LOG_TRIVIAL(debug) << "notch abord: too big angle\n"; return; } check_angle = start_point.ccw_angle(end_point, next_point); } assert(end_point != start_point); assert(end_point != next_point); - std::cout << "angle is " << check_angle << "\n"; if ((is_hole_loop ? -check_angle : check_angle) > this->m_config.seam_notch_angle.value * PI / 180.) { - std::cout << "notch abord: too big angle\n"; + BOOST_LOG_TRIVIAL(debug) << "notch abord: too big angle\n"; return; } - std::cout << "angle is okay! " << check_angle<< " ? "<< (this->m_config.seam_notch_angle.value * PI / 180.) << "\n"; - //std::cout << "points:" << building_paths.back().polyline.points.size() << "\n"; - //std::cout << "angle between " << unscaled(building_paths.back().polyline.points[2]).x() << ":" << unscaled(building_paths.back().polyline.points[2]).y() << " -> " - // << unscaled(building_paths.back().polyline.points[0]).x() << ":" << unscaled(building_paths.back().polyline.points[0]).y() << " -> " - // << unscaled(building_paths.back().polyline.points[1]).x() << ":" << unscaled(building_paths.back().polyline.points[1]).y() << " = " - // << building_paths.back().polyline.points[0].ccw_angle(building_paths.back().polyline.points[2], building_paths.back().polyline.points[1]) << "rad = " - // << int(building_paths.back().polyline.points[0].ccw_angle(building_paths.back().polyline.points[2], building_paths.back().polyline.points[1]) * 180 / PI) << "°\n"; - //std::cout << "angle between " << unscaled(building_paths.back().polyline.points[0]).x() << ":" << unscaled(building_paths.back().polyline.points[0]).y() << " -> " - // << unscaled(building_paths.back().polyline.points[1]).x() << ":" << unscaled(building_paths.back().polyline.points[1]).y() << " -> " - // << unscaled(building_paths.back().polyline.points[2]).x() << ":" << unscaled(building_paths.back().polyline.points[2]).y() << " = " - // << building_paths.back().polyline.points[1].ccw_angle(building_paths.back().polyline.points[0], building_paths.back().polyline.points[2]) << "rad = " - // << int(building_paths.back().polyline.points[1].ccw_angle(building_paths.back().polyline.points[0], building_paths.back().polyline.points[2]) * 180 / PI) << "°\n"; - //std::cout << "angle between " << unscaled(building_paths.back().polyline.points[1]).x() << ":" << unscaled(building_paths.back().polyline.points[1]).y() << " -> " - // << unscaled(building_paths.back().polyline.points[2]).x() << ":" << unscaled(building_paths.back().polyline.points[2]).y() << " -> " - // << unscaled(building_paths.back().polyline.points[0]).x() << ":" << unscaled(building_paths.back().polyline.points[0]).y() << " = " - // << building_paths.back().polyline.points[2].ccw_angle(building_paths.back().polyline.points[1], building_paths.back().polyline.points[0]) << "rad = " - // << int(building_paths.back().polyline.points[2].ccw_angle(building_paths.back().polyline.points[1], building_paths.back().polyline.points[0]) * 180 / PI) << "°\n"; - //check if the point is inside bool is_inside = original_loop.polygon().contains(moved_start) && original_loop.polygon().contains(moved_end); - std::cout << "is_inside? " << is_inside << "\n"; if ( (is_hole_loop && is_inside) || (!is_hole_loop && !is_inside) ) { - std::cout << "notch abord: not inside\n"; + BOOST_LOG_TRIVIAL(debug) << "notch abord: not inside\n"; return; } // set new start point @@ -4038,7 +4024,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, Point control_start_point = start_point; Line start_line(start_point, next_point); if (start_line.length() > notch_value * 2) { - std::cout << "start is in the first segment\n"; control_start_point = start_line.point_at(notch_value); //TODO: here, the arc is invalidaded, please change that to adapt the arc instead of removing all. building_paths.front().polyline.set_points().front() = start_line.point_at(notch_value * 2); @@ -4053,7 +4038,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, if (push_way_ctrl_dist < 0) { control_start_point = start_line.point_at(notch_value); } - std::cout << "start is not in the first segment\n"; if (building_paths.front().polyline.size() > 2) { building_paths.front().polyline.clip_first_point(); } @@ -4068,7 +4052,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, } } if (next_line.length() > push_way_dist) { - std::cout << "start is in the next segment\n"; //TODO: here, the arc is invalidaded, please change that to adapt the arc instead of removing all. building_paths.front().polyline.set_points().front() = next_line.point_at(push_way_dist); if (building_paths.front().polyline.has_arc() && building_paths.front().polyline.get_arc().front().path_type != Slic3r::Geometry::EMovePathType::Linear_move) { @@ -4077,9 +4060,7 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, push_way_dist = 0; good_start_point = true; } else { - std::cout << "start is not in the next segment\n"; if (std::abs(next_line.a.ccw_angle(start_point, next_line.b) - PI) > PI * 0.4) { - std::cout << " stop start search, angle is " << (next_line.a.ccw_angle(start_point, next_line.b) * 180 / PI) << " => " << (std::abs(next_line.a.ccw_angle(start_point, next_line.b) - PI) * 180 / PI) << " > " << (180 * 0.4) << "\n"; // if angle is sharp (not near 180°), stop search break; } @@ -4106,12 +4087,7 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, Point control_end_point = end_point; Line end_line(end_point, prev_point); coordf_t length_clipped = 0; - static int isazfqsdqs = 0; - std::stringstream stri; - stri << m_layer_index << "_" << is_hole_loop << "_" << isazfqsdqs++ << "_Nnotch" << ".svg"; - SVG svg1(stri.str()); if (end_line.length() > notch_value * 2) { - std::cout << "end is in the first segment\n"; control_end_point = end_line.point_at(notch_value); //TODO: here, the arc is invalidaded, please change that to adapt the arc instead of removing all. building_paths.back().polyline.set_points().back() = end_line.point_at(notch_value * 2); @@ -4120,7 +4096,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, } good_end_point = true; } else { - std::cout << "end is NOT in the first segment\n"; // move the other point further away double push_way_dist = notch_value * 2; double push_way_ctrl_dist = notch_value; @@ -4131,7 +4106,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, // remove a point until it's enough, then displace it at the right pos while (push_way_dist > 0 && current_end_line.polyline.size() > 1) { Line next_line(current_end_line.polyline.back(), current_end_line.polyline.get_points()[current_end_line.polyline.size() - 2]); - svg1.draw(next_line, "green", scale_d(0.1)); //try to get the control point (to create a curve) if (push_way_ctrl_dist > 0) { if (next_line.length() > push_way_ctrl_dist) { @@ -4143,7 +4117,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, } //try to get the end point if (next_line.length() > push_way_dist) { - std::cout << "end is in the next segment\n"; //TODO: here, the arc is invalidaded, please change that to adapt the arc instead of removing all. current_end_line.polyline.set_points().back() = next_line.point_at(push_way_dist); if (current_end_line.polyline.has_arc() && current_end_line.polyline.get_arc().back().path_type != Slic3r::Geometry::EMovePathType::Linear_move) { @@ -4152,10 +4125,7 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, push_way_dist = 0; good_end_point = true; } else { - std::cout << "end is not in the next segment\n"; if (end_point != next_line.a && std::abs(next_line.a.ccw_angle(end_point, next_line.b) - PI) > PI * 0.4) { - svg1.draw(Polyline{end_point, next_line.a, next_line.b}, "red", scale_d(0.05)); - std::cout << " stop end search, angle is " << (next_line.a.ccw_angle(end_point, next_line.b) * 180 / PI) << " => " << (std::abs(next_line.a.ccw_angle(end_point, next_line.b) - PI) * 180 / PI) << " > " << (180 * 0.4) << "\n"; //if clipped, full clip -> don't need to do anything. // if angle is sharp (not near 180°), stop search break; @@ -4178,7 +4148,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, assert(!building_paths.empty()); } while (push_way_dist > 0 && building_paths.size() > 0 && polyline_removed); if (push_way_dist > 0) { - std::cout << "end is too short, guess a control point\n"; //push as much as possible Line next_line(building_paths.back().last_point(), building_paths.back().polyline.get_points()[building_paths.back().polyline.size() - 2]); //TODO: here, the arc is invalidaded, please change that to adapt the arc instead of removing all. @@ -4188,12 +4157,9 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, } } if (push_way_ctrl_dist > 0) { - std::cout << "end is too short, try to advance a bit more\n"; control_end_point = Line(end_point, building_paths.back().polyline.back()).midpoint(); } } - svg1.Close(); - std::cout << "good_start_point=" << good_start_point << ", good_end_point=" << good_end_point << "\n"; auto create_new_extrusion = [](ExtrusionPaths& paths, const ExtrusionPath& model, float ratio, const Point& start, const Point& end) { // add notch extrutsions paths.emplace_back(model); @@ -4206,11 +4172,6 @@ void GCode::seam_notch(const ExtrusionLoop& original_loop, path.mm3_per_mm = path.mm3_per_mm * ratio; }; - static int isazfn = 0; - std::stringstream stri1; - stri1 <id(), nozzle_diam)); + if (original_loop.role() == erExternalPerimeter) { + coordf_t clip_length_external = scale_(m_config.seam_gap_external.get_abs_value(m_writer.tool()->id(), unscaled(clip_length))); + if (clip_length_external > 0) { + clip_length = clip_length_external; + } + } coordf_t min_clip_length = scale_(nozzle_diam) * 0.15; if (clip_length > full_loop_length / 4) { @@ -4656,15 +4619,29 @@ void GCode::add_wipe_points(const std::vector& paths) { std::string GCode::extrude_multi_path(const ExtrusionMultiPath &multipath, const std::string &description, double speed) { #if _DEBUG + assert(!multipath.empty()); + assert(!multipath.paths.front().polyline.empty()); for (auto it = std::next(multipath.paths.begin()); it != multipath.paths.end(); ++it) { assert(it->polyline.size() >= 2); assert(std::prev(it)->polyline.back() == it->polyline.front()); } #endif - // extrude along the path std::string gcode; - for (const ExtrusionPath &path : multipath.paths) { - gcode += extrude_path(path, description, speed); + //test if we reverse + if (m_last_pos_defined && multipath.can_reverse() + && multipath.first_point().distance_to_square(m_last_pos) > multipath.last_point().distance_to_square(m_last_pos)) { + //reverse to get a shorter point (hopefully there is still no feature that choose a point that need no perimeter crossing before). + // extrude along the reversedpath + for (size_t idx_path = multipath.paths.size() - 1; idx_path < multipath.paths.size(); --idx_path) { + assert(multipath.paths[idx_path].can_reverse()); + //extrude_path will reverse the path by itself, no need to copy it do to it here. + gcode += extrude_path(multipath.paths[idx_path], description, speed); + } + } else { + // extrude along the path + for (const ExtrusionPath& path : multipath.paths) { + gcode += extrude_path(path, description, speed); + } } add_wipe_points(multipath.paths); // reset acceleration @@ -4716,7 +4693,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, const std::stri } void GCode::use(const ExtrusionEntityCollection &collection) { - if (!collection.can_sort() || collection.role() == erMixed) { + if (!collection.can_sort() || collection.role() == erMixed || collection.entities().size() <= 1) { for (const ExtrusionEntity* next_entity : collection.entities()) { next_entity->visit(*this); } @@ -4731,6 +4708,13 @@ void GCode::use(const ExtrusionEntityCollection &collection) { std::string GCode::extrude_path(const ExtrusionPath &path, const std::string &description, double speed_mm_per_sec) { std::string gcode; ExtrusionPath simplifed_path = path; + + //check if we should reverse it + if (m_last_pos_defined && path.can_reverse() + && simplifed_path.first_point().distance_to_square(m_last_pos) > simplifed_path.last_point().distance_to_square(m_last_pos)) { + simplifed_path.reverse(); + } + // simplify with gcode_resolution (not used yet). Simplify by jusntion deviation before the g1/sec count, to be able to use that decimation to reduce max_gcode_per_second triggers. // But as it can be visible on cylinders, should only be called if a max_gcode_per_second trigger may come. //simplifed_path.simplify(m_scaled_gcode_resolution); diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 2df66066fc5..0400cfac0fa 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1857,7 +1857,6 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon if (doforeach(paths[paths.size() - 2], paths.back(), paths.front())) { paths.erase(paths.end() - 1); if (paths.back().height == paths.front().height) { - //paths.front().polyline.points.insert(paths.front().polyline.points.begin(), paths.back().polyline.points.begin(), paths.back().polyline.points.end() - 1); paths.back().polyline.append(paths.front().polyline); paths.front().polyline.swap(paths.back().polyline); paths.erase(paths.end() - 1); @@ -1867,7 +1866,6 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon if (doforeach(paths.back(), paths.front(), paths[1])) { paths.erase(paths.begin()); if (paths.back().height == paths.front().height) { - //paths.front().polyline.points.insert(paths.front().polyline.points.begin(), paths.back().polyline.points.begin(), paths.back().polyline.points.end() - 1); paths.back().polyline.append(paths.front().polyline); paths.front().polyline.swap(paths.back().polyline); paths.erase(paths.end() - 1); @@ -1888,13 +1886,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon //merge to previous assert(prev.last_point() == curr.first_point()); assert(curr.polyline.size() > 1); - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); } else { //merge to next assert(curr.last_point() == next.first_point()); assert(curr.polyline.size() > 1); - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); } @@ -1902,16 +1898,18 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon } else if(((int)curr.height) % 2 == 1 && curr.length() > ok_length){ curr.height++; if (prev.height == curr.height) { - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); + return true; } else if (next.height == curr.height) { - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); + return true; + } else { + return false; } - return true; + } else { + return false; } - return false; }); foreach(paths, [](ExtrusionPath& prev, ExtrusionPath& curr, ExtrusionPath& next) { @@ -1921,13 +1919,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon //merge to previous assert(prev.last_point() == curr.first_point()); assert(curr.polyline.size() > 1); - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); } else { //merge to next assert(curr.last_point() == next.first_point()); assert(curr.polyline.size() > 1); - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); } @@ -1942,13 +1938,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon //merge to previous assert(prev.last_point() == curr.first_point()); assert(curr.polyline.size() > 1); - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); } else { //merge to next assert(curr.last_point() == next.first_point()); assert(curr.polyline.size() > 1); - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); } @@ -1960,12 +1954,10 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const Polyline& loop_polygon if(paths.size() == 2){ double min_length = this->perimeter_flow.scaled_width() * 2; if (paths.front().length() < min_length) { - //paths.back().polyline.points.insert(paths.back().polyline.points.begin(), paths.front().polyline.points.begin(), paths.front().polyline.points.end() - 1); paths.front().polyline.append(paths.back().polyline); paths.back().polyline.swap(paths.front().polyline); paths.erase(paths.begin()); }else if (paths.back().length() < min_length) { - //paths.front().polyline.points.insert(paths.front().polyline.points.end(), paths.back().polyline.points.begin() + 1, paths.back().polyline.points.end()); paths.front().polyline.append(paths.back().polyline); paths.erase(paths.begin() + 1); } @@ -2175,7 +2167,6 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar paths.erase(paths.begin() + i); i--; if (paths[i].height == paths[i + 1].height) { - //paths[i].polyline.points.insert(paths[i].polyline.points.end(), paths[i + 1].polyline.points.begin() + 1, paths[i + 1].polyline.points.end()); paths[i].polyline.append(paths[i + 1].polyline); paths.erase(paths.begin() + i + 1); } @@ -2186,7 +2177,6 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar if (doforeach(paths[paths.size() - 2], paths.back(), paths.front())) { paths.erase(paths.end() - 1); if (paths.back().height == paths.front().height) { - //paths.front().polyline.points.insert(paths.front().polyline.points.begin(), paths.back().polyline.points.begin(), paths.back().polyline.points.end() - 1); paths.back().polyline.append(paths.front().polyline); paths.front().polyline.swap(paths.back().polyline); paths.erase(paths.end() - 1); @@ -2197,7 +2187,6 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar if (doforeach(paths.back(), paths.front(), paths[1])) { paths.erase(paths.begin()); if (paths.back().height == paths.front().height) { - //paths.front().polyline.points.insert(paths.front().polyline.points.begin(), paths.back().polyline.points.begin(), paths.back().polyline.points.end() - 1); paths.back().polyline.append(paths.front().polyline); paths.front().polyline.swap(paths.back().polyline); paths.erase(paths.end() - 1); @@ -2219,13 +2208,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar //merge to previous assert(prev.last_point() == curr.first_point()); assert(curr.polyline.size() > 1); - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); } else { //merge to next assert(curr.last_point() == next.first_point()); assert(curr.polyline.size() > 1); - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); } @@ -2233,10 +2220,8 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar } else if (((int)curr.height) % 2 == 1 && curr.length() > ok_length) { curr.height++; if (prev.height == curr.height) { - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); } else if (next.height == curr.height) { - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); } @@ -2252,13 +2237,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar //merge to previous assert(prev.last_point() == curr.first_point()); assert(curr.polyline.size() > 1); - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); } else { //merge to next assert(curr.last_point() == next.first_point()); assert(curr.polyline.size() > 1); - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); } @@ -2273,13 +2256,11 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar //merge to previous assert(prev.last_point() == curr.first_point()); assert(curr.polyline.size() > 1); - //prev.polyline.points.insert(prev.polyline.points.end(), curr.polyline.points.begin() + 1, curr.polyline.points.end()); prev.polyline.append(curr.polyline); } else { //merge to next assert(curr.last_point() == next.first_point()); assert(curr.polyline.size() > 1); - //next.polyline.points.insert(next.polyline.points.begin(), curr.polyline.points.begin(), curr.polyline.points.end() - 1); curr.polyline.append(next.polyline); next.polyline.swap(curr.polyline); } @@ -2291,12 +2272,10 @@ ExtrusionPaths PerimeterGenerator::create_overhangs(const ClipperLib_Z::Path& ar if (paths.size() == 2) { double min_length = this->perimeter_flow.scaled_width() * 2; if (paths.front().length() < min_length) { - //paths.back().polyline.points.insert(paths.back().polyline.points.begin(), paths.front().polyline.points.begin(), paths.front().polyline.points.end() - 1); paths.front().polyline.append(paths.back().polyline); paths.back().polyline.swap(paths.front().polyline); paths.erase(paths.begin()); } else if (paths.back().length() < min_length) { - //paths.front().polyline.points.insert(paths.front().polyline.points.end(), paths.back().polyline.points.begin() + 1, paths.back().polyline.points.end()); paths.front().polyline.append(paths.back().polyline); paths.erase(paths.begin() + 1); } diff --git a/src/libslic3r/Polyline.cpp b/src/libslic3r/Polyline.cpp index f63924744f9..037ea8de7d6 100644 --- a/src/libslic3r/Polyline.cpp +++ b/src/libslic3r/Polyline.cpp @@ -660,9 +660,13 @@ void PolylineOrArc::append(const PolylineOrArc& src) this->points = src.points; this->m_fitting_result = src.m_fitting_result; } else { - //BBS: append the first point to create connection first, update the fitting date as well + //BBS: append the first point to create connection first, update the fitting data as well if (src.front() != back()) { this->append(src.front()); + if (!this->m_fitting_result.empty()) { + assert(size() >= 2); + this->m_fitting_result.emplace_back(Slic3r::Geometry::PathFittingData{ this->points.size() - 2, this->points.size() - 1, Slic3r::Geometry::EMovePathType::Linear_move, Slic3r::Geometry::ArcSegment() }); + } } //BBS: append a polyline which has fitting data to a polyline without fitting data. //Then create a fake fitting data first, so that we can keep the fitting data in last polyline diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 05bc79e53b1..c26f6eee725 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4472,7 +4472,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Seam gap"); def->category = OptionCategory::extruders; def->tooltip = L("To avoid visible seam, the extrusion can be stoppped a bit before the end of the loop." - "\nCan be a mm or a % of the current extruder diameter."); + "\nCan be a mm or a % of the current extruder diameter."); def->sidetext = L("mm or %"); def->min = 0; def->max_literal = { 5, false }; @@ -4480,6 +4480,19 @@ void PrintConfigDef::init_fff_params() def->is_vector_extruder = true; def->set_default_value(new ConfigOptionFloatsOrPercents{ FloatOrPercent{15,true} }); + def = this->add("seam_gap_external", coFloatsOrPercents); + def->label = L("Seam gap for external perimeters"); + def->category = OptionCategory::extruders; + def->tooltip = L("To avoid visible seam, the extrusion can be stoppped a bit before the end of the loop." + "\n this setting is enforced only for external perimeter. It overrides 'seam_gap' if different than 0" + "\nCan be a mm or a % of the current seam gap."); + def->sidetext = L("mm or %"); + def->min = 0; + def->max_literal = { 5, false }; + def->mode = comExpert | comSuSi; + def->is_vector_extruder = true; + def->set_default_value(new ConfigOptionFloatsOrPercents{ FloatOrPercent{0,false} }); + def = this->add("seam_notch_all", coFloatOrPercent); def->label = L("for everything"); def->full_label = L("Seam notch"); @@ -6240,6 +6253,7 @@ void PrintConfigDef::init_extruder_option_keys() "retract_restart_extra_toolchange", "retract_speed", "seam_gap", + "seam_gap_external", "tool_name", "wipe", "wipe_extra_perimeter", @@ -7652,6 +7666,7 @@ std::unordered_set prusa_export_to_remove_keys = { "retract_lift_top", "seam_angle_cost", "seam_gap", +"seam_gap_external", "seam_notch_all", "seam_notch_angle", "seam_notch_inner", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 4db57fe42ff..66aa1a32098 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1201,6 +1201,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionInt, skirt_height)) ((ConfigOptionFloatOrPercent, skirt_extrusion_width)) ((ConfigOptionFloatsOrPercents, seam_gap)) + ((ConfigOptionFloatsOrPercents, seam_gap_external)) ((ConfigOptionInt, skirts)) ((ConfigOptionFloats, slowdown_below_layer_time)) ((ConfigOptionBool, spiral_vase)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 037ce25cb87..b727cca6f8f 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -965,6 +965,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "infill_connection_solid" || opt_key == "infill_connection_top" || opt_key == "seam_gap" + || opt_key == "seam_gap_external" || opt_key == "top_infill_extrusion_spacing" || opt_key == "top_infill_extrusion_width" ) { steps.emplace_back(posInfill); diff --git a/src/slic3r/GUI/ConfigWizard.hpp b/src/slic3r/GUI/ConfigWizard.hpp index d2d8711af7a..52d2ab1f364 100644 --- a/src/slic3r/GUI/ConfigWizard.hpp +++ b/src/slic3r/GUI/ConfigWizard.hpp @@ -14,7 +14,7 @@ class PresetUpdater; namespace GUI { -#define ALLOW_PRUSA_FIRST "PrusaResearch" +//#define ALLOW_PRUSA_FIRST "PrusaResearch" class ConfigWizard: public DPIDialog {