From 49e4e20a1b2c5427e37c7bd8994c8eb93131af80 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 11 Dec 2021 14:48:11 +0100 Subject: Fix too detailed gapfill * add resolution_internal to control it (and gyroid) * disallow gapfill lines to be split --- resources/ui_layout/print.ui | 1 + src/libslic3r/ClipperUtils.cpp | 2 +- src/libslic3r/ExtrusionEntity.cpp | 2 +- src/libslic3r/ExtrusionEntityCollection.cpp | 4 +- src/libslic3r/ExtrusionEntityCollection.hpp | 20 +-- src/libslic3r/Fill/Fill.cpp | 14 +-- src/libslic3r/Fill/FillBase.cpp | 6 +- src/libslic3r/Fill/FillConcentric.cpp | 4 +- src/libslic3r/Fill/FillGyroid.cpp | 24 ++-- src/libslic3r/Fill/FillRectilinear.cpp | 14 +-- src/libslic3r/Fill/FillSmooth.cpp | 4 +- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/Line.hpp | 2 +- src/libslic3r/MedialAxis.cpp | 138 ++++++++++++++------- src/libslic3r/MedialAxis.hpp | 2 +- src/libslic3r/PerimeterGenerator.cpp | 22 +++- src/libslic3r/Preset.cpp | 3 +- src/libslic3r/Print.cpp | 9 +- src/libslic3r/PrintConfig.cpp | 13 +- src/libslic3r/PrintConfig.hpp | 2 + tests/libslic3r/test_amf.cpp | 21 ++-- tests/libslic3r/test_clipper_utils.cpp | 14 ++- .../libslic3r/test_elephant_foot_compensation.cpp | 22 ++-- tests/libslic3r/test_geometry.cpp | 89 +++++++++++-- 24 files changed, 301 insertions(+), 133 deletions(-) diff --git a/resources/ui_layout/print.ui b/resources/ui_layout/print.ui index b5f893b70..622c612b8 100644 --- a/resources/ui_layout/print.ui +++ b/resources/ui_layout/print.ui @@ -83,6 +83,7 @@ group:Layer height setting:first_layer_height group:Filtering setting:resolution + setting:resolution_internal setting:model_precision setting:slice_closing_radius group:Modifying slices diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 431c4ed0f..078c2d3f2 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -231,7 +231,7 @@ ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType co.ArcTolerance = miterLimit; else co.MiterLimit = miterLimit; - double delta_scaled = delta * float(CLIPPER_OFFSET_SCALE); + double delta_scaled = delta * double(CLIPPER_OFFSET_SCALE); co.ShortestEdgeLength = double(std::abs(delta_scaled * CLIPPER_OFFSET_SHORTEST_EDGE_FACTOR)); co.AddPaths(input, joinType, endType); ClipperLib::Paths retval; diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 6b0611cca..5ed878499 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -376,7 +376,7 @@ void ExtrusionPrinter::use(const ExtrusionEntityCollection &collection) { if (i != 0) ss << ","; collection.entities[i]->visit(*this); } - if(collection.no_sort) ss<<", no_sort=true"; + if(!collection.can_sort()) ss<<", no_sort=true"; ss << "}"; } diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index c31b06966..1dd41f67f 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -61,7 +61,7 @@ void ExtrusionEntityCollection::reverse() { // Don't reverse it if it's a loop, as it doesn't change anything in terms of elements ordering // and caller might rely on winding order - if (!ptr->can_reverse()) + if (ptr->can_reverse() && !ptr->is_loop()) ptr->reverse(); } std::reverse(this->entities.begin(), this->entities.end()); @@ -151,7 +151,7 @@ ExtrusionEntityCollection ExtrusionEntityCollection::flatten(bool preserve_order } void FlatenEntities::use(const ExtrusionEntityCollection &coll) { - if ((coll.no_sort || this->to_fill.no_sort) && preserve_ordering) { + if ((!coll.can_sort() || !this->to_fill.can_sort()) && preserve_ordering) { FlatenEntities unsortable(coll, preserve_ordering); for (const ExtrusionEntity* entity : coll.entities) { entity->visit(unsortable); diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 08d2fddba..3e625eeeb 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -24,6 +24,11 @@ inline ExtrusionEntitiesPtr filter_by_extrusion_role(const ExtrusionEntitiesPtr class ExtrusionEntityCollection : public ExtrusionEntity { +private: + // set to tru to forbit to reorder and reverse all entities indie us. + bool no_sort; + // even if no_sort, allow to reverse() us (and our entities if they allow it, but they should) + bool no_reverse; public: virtual ExtrusionEntityCollection* clone() const override { return new ExtrusionEntityCollection(*this); } // Create a new object, initialize it with this object using the move semantics. @@ -33,14 +38,13 @@ public: /// Owned ExtrusionEntities and descendent ExtrusionEntityCollections. /// Iterating over this needs to check each child to see if it, too is a collection. ExtrusionEntitiesPtr entities; // we own these entities - bool no_sort; - ExtrusionEntityCollection(): no_sort(false) {} - ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort) { this->append(other.entities); } - ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {} + 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) : entities(std::move(other.entities)), no_sort(other.no_sort), no_reverse(other.no_reverse) {} explicit ExtrusionEntityCollection(const ExtrusionPaths &paths); ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other); ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) - { this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; } + { this->entities = std::move(other.entities); this->no_sort = other.no_sort; this->no_reverse = other.no_reverse; return *this; } ~ExtrusionEntityCollection() { clear(); } /// Operator to convert and flatten this collection to a single vector of ExtrusionPaths. @@ -55,7 +59,9 @@ public: } return out; } - bool can_reverse() const override { return !this->no_sort; } + 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; } bool empty() const { return this->entities.empty(); } void clear(); void swap (ExtrusionEntityCollection &c); @@ -152,7 +158,7 @@ class FlatenEntities : public ExtrusionVisitorConst { public: FlatenEntities(bool preserve_ordering) : preserve_ordering(preserve_ordering) {} FlatenEntities(ExtrusionEntityCollection pattern, bool preserve_ordering) : preserve_ordering(preserve_ordering) { - to_fill.no_sort = pattern.no_sort; + to_fill.set_can_sort_reverse(pattern.can_sort(), pattern.can_reverse()); } ExtrusionEntityCollection get() { return to_fill; diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index d09a9b9b6..93360eb75 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -413,9 +413,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: m_regions[region_id]->fills.append(fills_by_priority[0]->entities); delete fills_by_priority[0]; } else { - m_regions[region_id]->fills.no_sort = true; + m_regions[region_id]->fills.set_can_sort_reverse(false, false); ExtrusionEntityCollection* eec = new ExtrusionEntityCollection(); - eec->no_sort = true; + eec->set_can_sort_reverse(false, false); m_regions[region_id]->fills.entities.push_back(eec); for (ExtrusionEntityCollection* per_priority : fills_by_priority) { if (!per_priority->entities.empty()) @@ -580,9 +580,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: for (LayerRegion *layerm : m_regions) for (const ExtrusionEntity *thin_fill : layerm->thin_fills.entities) { ExtrusionEntityCollection *collection = new ExtrusionEntityCollection(); - if (layerm->fills.no_sort && layerm->fills.entities.size() > 0 && layerm->fills.entities[0]->is_collection()) { + if (!layerm->fills.can_sort() && layerm->fills.entities.size() > 0 && layerm->fills.entities[0]->is_collection()) { ExtrusionEntityCollection* no_sort_fill = static_cast(layerm->fills.entities[0]); - if (no_sort_fill->no_sort && no_sort_fill->entities.size() > 0 && no_sort_fill->entities[0]->is_collection()) + if (!no_sort_fill->can_sort() && no_sort_fill->entities.size() > 0 && no_sort_fill->entities[0]->is_collection()) static_cast(no_sort_fill->entities[0])->entities.push_back(collection); } else layerm->fills.entities.push_back(collection); @@ -593,7 +593,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: for (LayerRegion *layerm : m_regions) for (size_t i1 = 0; i1 < layerm->fills.entities.size(); ++i1) { assert(dynamic_cast(layerm->fills.entities[i1]) != nullptr); - if (layerm->fills.no_sort && layerm->fills.entities.size() > 0 && i1 == 0){ + if (!layerm->fills.can_sort() && layerm->fills.entities.size() > 0 && i1 == 0){ ExtrusionEntityCollection* no_sort_fill = static_cast(layerm->fills.entities[0]); assert(no_sort_fill != nullptr); assert(!no_sort_fill->empty()); @@ -601,7 +601,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: ExtrusionEntityCollection* priority_fill = dynamic_cast(no_sort_fill->entities[i2]); assert(priority_fill != nullptr); assert(!priority_fill->empty()); - if (no_sort_fill->no_sort) { + if (!no_sort_fill->can_sort()) { for (size_t i3 = 0; i3 < priority_fill->entities.size(); ++i3) assert(dynamic_cast(priority_fill->entities[i3]) != nullptr); } @@ -780,7 +780,7 @@ void Layer::make_ironing() ExtrusionEntityCollection *eec = new ExtrusionEntityCollection(); ironing_params.layerm->ironings.entities.push_back(eec); // Don't sort the ironing infill lines as they are monotonicly ordered. - eec->no_sort = true; + eec->set_can_sort_reverse(false, false); extrusion_entities_append_paths( eec->entities, std::move(polylines), erIroning, diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 26b73bd25..1e6dff4aa 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -211,7 +211,7 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ // Save into layer. auto *eec = new ExtrusionEntityCollection(); /// pass the no_sort attribute to the extrusion path - eec->no_sort = this->no_sort(); + eec->set_can_sort_reverse(!this->no_sort(), !this->no_sort()); /// add it into the collection out.push_back(eec); //get the role @@ -269,7 +269,7 @@ Fill::do_gap_fill(const ExPolygons& gapfill_areas, const FillParams& params, Ext } #endif - ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, params.flow); + ExtrusionEntityCollection gap_fill = thin_variable_width(polylines_gapfill, erGapFill, params.flow, scale_t(params.config->get_computed_value("resolution_internal"))); //set role if needed /*if (params.role != erSolidInfill) { ExtrusionSetRole set_good_role(params.role); @@ -278,7 +278,7 @@ Fill::do_gap_fill(const ExPolygons& gapfill_areas, const FillParams& params, Ext //move them into the collection if (!gap_fill.entities.empty()) { ExtrusionEntityCollection* coll_gapfill = new ExtrusionEntityCollection(); - coll_gapfill->no_sort = this->no_sort(); + coll_gapfill->set_can_sort_reverse(!this->no_sort(), !this->no_sort()); coll_gapfill->append(std::move(gap_fill.entities)); coll_out.push_back(coll_gapfill); } diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 6da55e454..c8616f42f 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -128,7 +128,7 @@ FillConcentricWGapFill::fill_surface_extrusion( ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection(); - coll_nosort->no_sort = true; //can be sorted inside the pass + coll_nosort->set_can_sort_reverse(false, false); //can be sorted inside the pass extrusion_entities_append_loops( coll_nosort->entities, loops, good_role, @@ -154,7 +154,7 @@ FillConcentricWGapFill::fill_surface_extrusion( } } if (!polylines.empty() && !is_bridge(good_role)) { - ExtrusionEntityCollection gap_fill = thin_variable_width(polylines, erGapFill, params.flow); + ExtrusionEntityCollection gap_fill = thin_variable_width(polylines, erGapFill, params.flow, scale_t(params.config->get_computed_value("resolution_internal"))); //set role if needed if (good_role != erSolidInfill) { ExtrusionSetRole set_good_role(good_role); diff --git a/src/libslic3r/Fill/FillGyroid.cpp b/src/libslic3r/Fill/FillGyroid.cpp index c51cdf914..0ff0972c5 100644 --- a/src/libslic3r/Fill/FillGyroid.cpp +++ b/src/libslic3r/Fill/FillGyroid.cpp @@ -101,13 +101,8 @@ static std::vector make_one_period(double width, double scaleFactor, doub return points; } -static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height) +static Polylines make_gyroid_waves(coordf_t gridZ, coordf_t scaleFactor, double width, double height, double tolerance) { - const double scaleFactor = scale_(line_spacing) / density_adjusted; - - // tolerance in scaled units. clamp the maximum tolerance as there's - // no processing-speed benefit to do so beyond a certain point - const double tolerance = std::min(line_spacing / 2, FillGyroid::PatternTolerance) / unscale(scaleFactor); //scale factor for 5% : 8 712 388 // 1z = 10^-6 mm ? @@ -167,13 +162,22 @@ void FillGyroid::_fill_surface_single( // align bounding box to a multiple of our grid module bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance))); + // tolerance in scaled units. clamp the maximum tolerance as there's + // no processing-speed benefit to do so beyond a certain point + const coordf_t scaleFactor = scale_d(this->get_spacing()) / density_adjusted; + const double tolerance_old = std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) / unscaled(scaleFactor); + const double tolerance_old2 = std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) * density_adjusted / this->get_spacing(); + const double tolerance = params.config->get_computed_value("resolution_internal") * density_adjusted / this->get_spacing(); + std::cout << "gyroid tolerance: " << tolerance_old << " == " << tolerance_old2 << " ? "<< tolerance << "\n"; + std::cout << "this->get_spacing(): " << this->get_spacing() << " , scaleFactor= " << unscaled(scaleFactor) << " , min(spa, 0.2)= " << std::min(this->get_spacing() / 2, FillGyroid::PatternTolerance) << "\n"; + // generate pattern Polylines polylines = make_gyroid_waves( - (double)scale_(this->z), - density_adjusted, - this->get_spacing(), + scale_d(this->z), + scaleFactor, ceil(bb.size()(0) / distance) + 1., - ceil(bb.size()(1) / distance) + 1.); + ceil(bb.size()(1) / distance) + 1., + tolerance); // shift the polyline to the grid origin for (Polyline &pl : polylines) diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 598ef50d1..6495dae72 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -3208,7 +3208,7 @@ FillRectilinearPeri::fill_surface_extrusion(const Surface *surface, const FillPa { ExtrusionEntityCollection *eecroot = new ExtrusionEntityCollection(); //you don't want to sort the extrusions: big infill first, small second - eecroot->no_sort = true; + eecroot->set_can_sort_reverse(false, false); // === extrude perimeter === Polylines polylines_1; @@ -3231,7 +3231,7 @@ FillRectilinearPeri::fill_surface_extrusion(const Surface *surface, const FillPa // Save into layer. ExtrusionEntityCollection *eec = new ExtrusionEntityCollection(); /// pass the no_sort attribute to the extrusion path - eec->no_sort = this->no_sort(); + eec->set_can_sort_reverse(!this->no_sort(), !this->no_sort()); /// add it into the collection eecroot->entities.push_back(eec); //get the role @@ -3263,7 +3263,7 @@ FillRectilinearPeri::fill_surface_extrusion(const Surface *surface, const FillPa // Save into layer. ExtrusionEntityCollection *eec = new ExtrusionEntityCollection(); /// pass the no_sort attribute to the extrusion path - eec->no_sort = this->no_sort(); + eec->set_can_sort_reverse(!this->no_sort(), !this->no_sort()); /// add it into the collection eecroot->entities.push_back(eec); //get the role @@ -3365,7 +3365,7 @@ FillRectilinearSawtooth::fill_surface_extrusion(const Surface *surface, const Fi if (!polylines_out.empty()) { ExtrusionEntityCollection *eec = new ExtrusionEntityCollection(); /// pass the no_sort attribute to the extrusion path - eec->no_sort = this->no_sort(); + eec->set_can_sort_reverse(!this->no_sort(), !this->no_sort()); ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); @@ -3489,7 +3489,7 @@ FillRectilinearWGapFill::split_polygon_gap_fill(const Surface &surface, const Fi void FillRectilinearWGapFill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const { ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection(); - coll_nosort->no_sort = true; //can be sorted inside the pass + coll_nosort->set_can_sort_reverse(false, false); //can be sorted inside the pass but thew two pass need to be done one after the other ExtrusionRole good_role = getRoleFromSurfaceType(params, surface); //// remove areas for gapfill @@ -3558,9 +3558,9 @@ FillRectilinearWGapFill::fill_surface_extrusion(const Surface *surface, const Fi /// pass the no_sort attribute to the extrusion path //don't force monotonic if not top or bottom if (is_monotonic()) - eec->no_sort = true; + eec->set_can_sort_reverse(false, false); else - eec->no_sort = this->no_sort(); + eec->set_can_sort_reverse(!this->no_sort(), !this->no_sort()); extrusion_entities_append_paths( eec->entities, polylines_rectilinear, diff --git a/src/libslic3r/Fill/FillSmooth.cpp b/src/libslic3r/Fill/FillSmooth.cpp index 444e61132..2c6f54da8 100644 --- a/src/libslic3r/Fill/FillSmooth.cpp +++ b/src/libslic3r/Fill/FillSmooth.cpp @@ -24,7 +24,7 @@ namespace Slic3r { // Save into layer smoothing path. ExtrusionEntityCollection *eec = new ExtrusionEntityCollection(); - eec->no_sort = params.monotonic; + eec->set_can_sort_reverse(!params.monotonic, !params.monotonic); FillParams params_modifided = params; if (params.config != NULL && idx > 0) params_modifided.density /= (float)params.config->fill_smooth_width.get_abs_value(1); else if (params.config != NULL && idx == 0) params_modifided.density *= 1; @@ -131,7 +131,7 @@ namespace Slic3r { //create root node ExtrusionEntityCollection *eecroot = new ExtrusionEntityCollection(); //you don't want to sort the extrusions: big infill first, small second - eecroot->no_sort = true; + eecroot->set_can_sort_reverse(false, false); // first infill FillParams first_pass_params = params; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ee6eb01f0..edf1677b2 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -3455,7 +3455,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, const std::stri } void GCode::use(const ExtrusionEntityCollection &collection) { - if (collection.no_sort || collection.role() == erMixed) { + if (!collection.can_sort() || collection.role() == erMixed) { for (const ExtrusionEntity* next_entity : collection.entities) { next_entity->visit(*this); } diff --git a/src/libslic3r/Line.hpp b/src/libslic3r/Line.hpp index d93e37e7f..88a9d4db2 100644 --- a/src/libslic3r/Line.hpp +++ b/src/libslic3r/Line.hpp @@ -97,7 +97,7 @@ public: ThickLine(const Point& a, const Point& b) : Line(a, b), a_width(0), b_width(0) {} ThickLine(const Point& a, const Point& b, double wa, double wb) : Line(a, b), a_width(wa), b_width(wb) {} - double a_width, b_width; + coordf_t a_width, b_width; }; class Line3 diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index 1ed1320f7..a84b69204 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -1923,82 +1923,115 @@ MedialAxis::build(ThickPolylines &polylines_out) } ExtrusionEntityCollection -thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) +thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow, coord_t resolution_internal) { + assert(resolution_internal > SCALED_EPSILON); + // this value determines granularity of adaptive width, as G-code does not allow // variable extrusion within a single move; this value shall only affect the amount // of segments, and any pruning shall be performed before we apply this tolerance - const coord_t tolerance = 4 * SCALED_RESOLUTION;//scale_(0.05); + const coord_t tolerance = flow.scaled_width() / 10;//scale_(0.05); ExtrusionEntityCollection coll; for (const ThickPolyline &p : polylines) { ExtrusionPaths paths; ExtrusionPath path(role); ThickLines lines = p.thicklines(); - + + coordf_t saved_line_len = 0; for (int i = 0; i < (int)lines.size(); ++i) { ThickLine& line = lines[i]; - + const coordf_t line_len = line.length(); - if (line_len < SCALED_EPSILON) continue; - + const coordf_t prev_line_len = saved_line_len; + saved_line_len = line_len; + assert(line.a_width >= 0); assert(line.b_width >= 0); coord_t thickness_delta = std::abs(line.a_width - line.b_width); - if (thickness_delta > tolerance && ceil(float(thickness_delta) / float(tolerance)) > 2) { - const uint16_t segments = 1 + (uint16_t) std::min((uint32_t)16000, (uint32_t)ceil(float(thickness_delta) / float(tolerance))); - Points pp; - std::vector width; - { + + // split lines ? + if (resolution_internal < line_len) { + if (thickness_delta > tolerance && ceil(float(thickness_delta) / float(tolerance)) > 2) { + const uint16_t segments = 1 + (uint16_t)std::min((uint32_t)16000, (uint32_t)ceil(float(thickness_delta) / float(tolerance))); + Points pp; + std::vector width; + { + for (size_t j = 0; j < segments; ++j) { + pp.push_back(line.a.interpolate(((double)j) / segments, line.b)); + double percent_width = ((double)j) / (segments - 1); + width.push_back(line.a_width * (1 - percent_width) + line.b_width * percent_width); + } + pp.push_back(line.b); + + assert(pp.size() == segments + 1); + assert(width.size() == segments); + } + + // delete this line and insert new ones + lines.erase(lines.begin() + i); for (size_t j = 0; j < segments; ++j) { - pp.push_back(line.a.interpolate(((double)j) / segments, line.b)); - double percent_width = ((double)j) / (segments-1); - width.push_back(line.a_width * (1 - percent_width) + line.b_width * percent_width); + ThickLine new_line(pp[j], pp[j + 1]); + new_line.a_width = width[j]; + new_line.b_width = width[j]; + lines.insert(lines.begin() + i + j, new_line); } - pp.push_back(line.b); - assert(pp.size() == segments + 1); - assert(width.size() == segments); + // go back to the start of this loop iteration + --i; + continue; + } else if (thickness_delta > 0) { + //create a middle point + ThickLine new_line(line.a.interpolate(0.5, line.b), line.b); + new_line.a_width = line.b_width; + new_line.b_width = line.b_width; + line.b = new_line.a; + line.b_width = line.a_width; + lines.insert(lines.begin() + i + 1, new_line); + + // go back to the start of this loop iteration + --i; + continue; } - - // delete this line and insert new ones + } else if (i > 0 && resolution_internal > line_len + prev_line_len) { + ThickLine& prev_line = lines[i - 1]; + //merge lines? + coordf_t width = prev_line_len * (prev_line.a_width + prev_line.b_width) / 2; + width += line_len * (line.a_width + line.b_width) / 2; + prev_line.b = line.b; + coordf_t new_length = prev_line.length(); + width /= new_length; + prev_line.a_width = width; + prev_line.b_width = width; + saved_line_len = new_length; + //erase 'line' lines.erase(lines.begin() + i); - for (size_t j = 0; j < segments; ++j) { - ThickLine new_line(pp[j], pp[j + 1]); - new_line.a_width = width[j]; - new_line.b_width = width[j]; - lines.insert(lines.begin() + i + j, new_line); - } - --i; continue; } else if (thickness_delta > 0) { - //create a middle point - ThickLine new_line(line.a.interpolate(0.5, line.b), line.b); - new_line.a_width = line.b_width; - new_line.b_width = line.b_width; - line.b = new_line.a; + //set width as a middle-ground + line.a_width = (line.a_width + line.b_width) / 2; line.b_width = line.a_width; - lines.insert(lines.begin() + i + 1, new_line); - - --i; - continue; } + } + for (int i = 0; i < (int)lines.size(); ++i) { + ThickLine& line = lines[i]; + //gapfill : we want to be able to fill the voids (touching the perimeters), so the spacing is what we want. //thinwall: we want the extrusion to not go out of the polygon, so the width is what we want. // but we can't extrude with a negative spacing, so we have to gradually fall back to spacing if the width is too small. // default: extrude a thin wall that doesn't go outside of the specified width. - coordf_t wanted_width = unscale(line.a_width); + double wanted_width = unscaled(line.a_width); if (role == erGapFill) { // Convert from spacing to extrusion width based on the extrusion model // of a square extrusion ended with semi circles. - wanted_width = unscale(line.a_width) + flow.height * (1. - 0.25 * PI); + wanted_width = unscaled(line.a_width) + flow.height * (1. - 0.25 * PI); } else if (unscale(line.a_width) < 2 * flow.height * (1. - 0.25 * PI)) { //width (too) small, be sure to not extrude with negative spacing. //we began to fall back to spacing gradually even before the spacing go into the negative // to make extrusion1 < extrusion2 if width1 < width2 even if width2 is too small. - wanted_width = unscale(line.a_width)*0.35 + 1.3 * flow.height * (1. - 0.25 * PI); + wanted_width = unscaled(line.a_width)*0.35 + 1.3 * flow.height * (1. - 0.25 * PI); } if (path.polyline.points.empty()) { @@ -2012,7 +2045,7 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl path.width = flow.width; path.height = flow.height; } else { - thickness_delta = scale_(fabs(flow.width - wanted_width)); + coord_t thickness_delta = scale_t(fabs(flow.width - wanted_width)); if (thickness_delta <= tolerance / 2) { // the width difference between this line and the current flow width is // within the accepted tolerance @@ -2021,12 +2054,21 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl // we need to initialize a new line paths.emplace_back(std::move(path)); path = ExtrusionPath(role); - --i; + flow.width = wanted_width; + path.polyline.append(line.a); + path.polyline.append(line.b); + assert(flow.mm3_per_mm() == flow.mm3_per_mm()); + assert(flow.width == flow.width); + assert(flow.height == flow.height); + path.mm3_per_mm = flow.mm3_per_mm(); + path.width = flow.width; + path.height = flow.height; } } } if (path.polyline.is_valid()) paths.emplace_back(std::move(path)); + // Append paths to collection. if (!paths.empty()) { if (paths.front().first_point().coincides_with(paths.back().last_point())) { @@ -2034,11 +2076,21 @@ thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow fl } else { if (role == erThinWall){ //thin walls : avoid to cut them, please. + //also, keep the start, as the start should be already in a frontier where possible. ExtrusionEntityCollection unsortable_coll(paths); - unsortable_coll.no_sort = true; + unsortable_coll.set_can_sort_reverse(false, false); coll.append(unsortable_coll); - }else //gap fill : cut them as much as you want - coll.append(paths); + } else { + if (paths.size() <= 1) { + coll.append(paths); + } else { + ExtrusionEntityCollection unsortable_coll(paths); + //gap fill : can reverse, but refrain from cutting them as it creates a mess. + // I say that, but currently (false, true) does bad things. + unsortable_coll.set_can_sort_reverse(false, true); + coll.append(unsortable_coll); + } + } } } } diff --git a/src/libslic3r/MedialAxis.hpp b/src/libslic3r/MedialAxis.hpp index 69bd94268..18a7eb2f0 100644 --- a/src/libslic3r/MedialAxis.hpp +++ b/src/libslic3r/MedialAxis.hpp @@ -115,7 +115,7 @@ class MedialAxis { }; /// create a ExtrusionEntityCollection from ThickPolylines, discretizing the variable width into little sections (of 4*SCALED_RESOLUTION length) where needed. - ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow); + ExtrusionEntityCollection thin_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow, coord_t resolution_internal); } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 9f98eeff5..6ba0c0815 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -939,7 +939,7 @@ void PerimeterGenerator::process() // append thin walls if (!thin_walls.empty()) { ExtrusionEntityCollection tw = thin_variable_width - (thin_walls, erThinWall, this->ext_perimeter_flow); + (thin_walls, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_width/4, scale_t(this->print_config->resolution))); entities.append(tw.entities); thin_walls.clear(); @@ -1086,7 +1086,17 @@ void PerimeterGenerator::process() // create extrusion from lines if (!polylines.empty()) { ExtrusionEntityCollection gap_fill = thin_variable_width(polylines, - erGapFill, this->solid_infill_flow); + erGapFill, this->solid_infill_flow, scale_t(this->print_config->resolution_internal)); + + //{ + // static int isaqsdsdfsdfqzfn = 0; + // std::stringstream stri; + // stri << this->layer->id() << "_gapfill_" << isaqsdsdfsdfqzfn++ << ".svg"; + // SVG svg(stri.str()); + // svg.draw((surface.expolygon), "grey"); + // svg.draw(polylines, "blue"); + // svg.Close(); + //} this->gap_fill->append(gap_fill.entities); /* Make sure we don't infill narrow parts that are already gap-filled (we only consider this surface's gaps to reduce the diff() complexity). @@ -1478,7 +1488,7 @@ ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( // append thin walls to the nearest-neighbor search (only for first iteration) if (!thin_walls.empty()) { - ExtrusionEntityCollection tw = thin_variable_width(thin_walls, erThinWall, this->ext_perimeter_flow); + ExtrusionEntityCollection tw = thin_variable_width(thin_walls, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution))); coll.append(tw.entities); thin_walls.clear(); } @@ -1631,7 +1641,7 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions current_loop = last_loop; } virtual void use(ExtrusionEntityCollection &collection) override { - collection.no_sort = false; + collection.set_can_sort_reverse(true, true); //for each loop? (or other collections) for (ExtrusionEntity *entity : collection.entities) entity->visit(*this); @@ -1672,7 +1682,7 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions searcher.search_result.loop->paths.insert(searcher.search_result.loop->paths.begin() + 1 + searcher.search_result.idx_path, ExtrusionPath(poly_after, *searcher.search_result.path)); //create thin wall path exttrusion - ExtrusionEntityCollection tws = thin_variable_width({ tw }, erThinWall, this->ext_perimeter_flow); + ExtrusionEntityCollection tws = thin_variable_width({ tw }, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution))); ChangeFlow change_flow; if (tws.entities.size() == 1 && tws.entities[0]->is_loop()) { //loop, just add it @@ -1708,7 +1718,7 @@ void PerimeterGenerator::_merge_thin_walls(ExtrusionEntityCollection &extrusions } //now add thinwalls that have no anchor (make them reversable) - ExtrusionEntityCollection tws = thin_variable_width(not_added, erThinWall, this->ext_perimeter_flow); + ExtrusionEntityCollection tws = thin_variable_width(not_added, erThinWall, this->ext_perimeter_flow, std::max(ext_perimeter_flow.scaled_width() / 4, scale_t(this->print_config->resolution))); extrusions.append(tws.entities); } diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index d8be8af9e..45eace66b 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -627,9 +627,10 @@ const std::vector& Preset::print_options() "thin_walls_overlap", "thin_walls_speed", "thin_walls_merge", - //precision, spoothign + //precision, smoothing "model_precision", "resolution", + "resolution_internal", "curve_smoothing_precision", "curve_smoothing_cutoff_dist", "curve_smoothing_angle_convex", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 41b52b78a..904ebe45c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -234,9 +234,12 @@ bool Print::invalidate_state_by_config_options(const std::vector>& loops, c } else { ExtrusionEntityCollection* print_me_first = new ExtrusionEntityCollection(); parent->entities.push_back(print_me_first); - print_me_first->no_sort = true; + print_me_first->set_can_sort_reverse(false, false); for (Polyline& line : to_cut.lines) if (line.points.back() == line.points.front()) { ExtrusionPath path(erSkirt, mm3_per_mm, width, height); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d67c516fd..7d7974457 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3364,7 +3364,18 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->precision = 8; def->mode = comExpert; - def->set_default_value(new ConfigOptionFloat(0.002)); + def->set_default_value(new ConfigOptionFloat(0.0125)); + + def = this->add("resolution_internal", coFloat); + def->label = L("Internal resolution"); + def->category = OptionCategory::slicing; + def->tooltip = L("Minimum detail resolution, used for internal strutures (gapfill and some infill patterns)." + "\nDon't put a too small value (0.05mm is way too low for many printers), as it may creates too many very small segments that may difficult to display and print."); + def->sidetext = L("mm"); + def->min = 0.001; + def->precision = 8; + def->mode = comExpert; + def->set_default_value(new ConfigOptionFloat(0.2)); def = this->add("retract_before_travel", coFloats); def->label = L("Minimum travel after retraction"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 8d5047e62..8e64e70ea 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1377,6 +1377,7 @@ public: ConfigOptionString printer_model; ConfigOptionString printer_notes; ConfigOptionFloat resolution; + ConfigOptionFloat resolution_internal; ConfigOptionFloats retract_before_travel; ConfigOptionBools retract_layer_change; ConfigOptionInt skirt_brim; @@ -1485,6 +1486,7 @@ protected: OPT_PTR(printer_model); OPT_PTR(printer_notes); OPT_PTR(resolution); + OPT_PTR(resolution_internal); OPT_PTR(retract_before_travel); OPT_PTR(retract_layer_change); OPT_PTR(seam_gap); diff --git a/tests/libslic3r/test_amf.cpp b/tests/libslic3r/test_amf.cpp index 48cb9d0d1..ff806d17f 100644 --- a/tests/libslic3r/test_amf.cpp +++ b/tests/libslic3r/test_amf.cpp @@ -1,3 +1,4 @@ +#define CATCH_CONFIG_DISABLE #include #include "test_utils.hpp" #include "libslic3r/Model.hpp" @@ -9,10 +10,11 @@ using namespace std::literals::string_literals; SCENARIO("Reading deflated AMF files", "[AMF]") { auto _tmp_config = DynamicPrintConfig{}; + auto _tmp_conf_sub = ConfigSubstitutionContext{ForwardCompatibilitySubstitutionRule::Disable}; GIVEN("Compressed AMF file of a 20mm cube") { auto model {new Slic3r::Model()}; WHEN("file is read") { - bool result_code = load_amf(get_model_path("test_amf/20mmbox_deflated.amf"s).c_str(), &_tmp_config, model, false); + bool result_code = load_amf(get_model_path("test_amf/20mmbox_deflated.amf"s).c_str(), &_tmp_config, &_tmp_conf_sub, model, false); THEN("Does not return false.") { REQUIRE(result_code == true); } @@ -21,7 +23,7 @@ SCENARIO("Reading deflated AMF files", "[AMF]") { } } WHEN("single file is read with some subdirectories") { - bool result_code = load_amf(get_model_path("test_amf/20mmbox_deflated-in_directories.amf"s).c_str(), &_tmp_config, model, false); + bool result_code = load_amf(get_model_path("test_amf/20mmbox_deflated-in_directories.amf"s).c_str(), &_tmp_config, &_tmp_conf_sub, model, false); THEN("Read returns false.") { REQUIRE(result_code == true); } @@ -30,7 +32,7 @@ SCENARIO("Reading deflated AMF files", "[AMF]") { } } WHEN("file is read with multiple files in the archive") { - bool result_code = load_amf(get_model_path("test_amf/20mmbox_deflated-mult_files.amf"s).c_str(), &_tmp_config, model, false); + bool result_code = load_amf(get_model_path("test_amf/20mmbox_deflated-mult_files.amf"s).c_str(), &_tmp_config, &_tmp_conf_sub, model, false); THEN("Read returns ture.") { REQUIRE(result_code == true); } @@ -43,7 +45,7 @@ SCENARIO("Reading deflated AMF files", "[AMF]") { GIVEN("Uncompressed AMF file of a 20mm cube") { auto model {new Slic3r::Model()}; WHEN("file is read") { - bool result_code = load_amf(get_model_path("test_amf/20mmbox.amf"s).c_str(), &_tmp_config, model, false); + bool result_code = load_amf(get_model_path("test_amf/20mmbox.amf"s).c_str(), &_tmp_config, &_tmp_conf_sub, model, false); THEN("Does not return false.") { REQUIRE(result_code == true); } @@ -52,7 +54,7 @@ SCENARIO("Reading deflated AMF files", "[AMF]") { } } WHEN("nonexistant file is read") { - bool result_code = load_amf(get_model_path("test_amf/20mmbox-doesnotexist.amf"s).c_str(), &_tmp_config, model, false); + bool result_code = load_amf(get_model_path("test_amf/20mmbox-doesnotexist.amf"s).c_str(), &_tmp_config, &_tmp_conf_sub, model, false); THEN("Read returns false.") { REQUIRE(result_code == false); } @@ -66,12 +68,13 @@ SCENARIO("Reading deflated AMF files", "[AMF]") { SCENARIO("Reading AMF file", "[AMF]") { auto _tmp_config = DynamicPrintConfig{}; + auto _tmp_conf_sub = ConfigSubstitutionContext{ ForwardCompatibilitySubstitutionRule::Disable }; GIVEN("badly formed AMF file (missing vertices)") { auto model {new Slic3r::Model()}; WHEN("AMF model is read") { - auto ret = Slic3r::load_amf(get_model_path("test_amf/5061-malicious.amf").c_str(), &_tmp_config, model, false); - THEN("read should return True") { - REQUIRE(ret); + auto ret = Slic3r::load_amf(get_model_path("test_amf/5061-malicious.amf").c_str(), &_tmp_config, &_tmp_conf_sub, model, false); + THEN("read should return False") { + REQUIRE(!ret); } } delete model; @@ -80,7 +83,7 @@ SCENARIO("Reading AMF file", "[AMF]") { auto model {new Slic3r::Model()}; WHEN("AMF model is read") { std::cerr << "TEST_DATA_DIR/test_amf/read-amf.amf"; - auto ret = Slic3r::load_amf(get_model_path("test_amf/read-amf.amf").c_str(), &_tmp_config, model, false); + auto ret = Slic3r::load_amf(get_model_path("test_amf/read-amf.amf").c_str(), &_tmp_config, &_tmp_conf_sub, model, false); THEN("read should return True") { REQUIRE(ret); } diff --git a/tests/libslic3r/test_clipper_utils.cpp b/tests/libslic3r/test_clipper_utils.cpp index f83d973b3..a6a43b6f2 100644 --- a/tests/libslic3r/test_clipper_utils.cpp +++ b/tests/libslic3r/test_clipper_utils.cpp @@ -1,3 +1,4 @@ +#define CATCH_CONFIG_DISABLE #include #include @@ -10,6 +11,11 @@ using namespace Slic3r; +// 32 bit = 10^9 +// 64 bits: 10^18 +// scale : 1 000 000 (10^6) ~ 2^20 +// clipper scale : 2^17 +// => clipper useful range: 2^26 ~ 10 000 000m => 10 000 km SCENARIO("test clipper limits", "[ClipperUtils]") { GIVEN("100mm square") { WHEN("offset") { @@ -23,11 +29,15 @@ SCENARIO("test clipper limits", "[ClipperUtils]") { THEN("offset 10000") { REQUIRE(offset(square, scale_(10000)).size() == 1); } + // every segment shorter than 0.5% of the offset will be cut. + // that means 500 for an offset of 100000 + // so from now, offsetting it will destroy evrything + // (since 2017) THEN("offset 100000") { - REQUIRE(offset(square, scale_(100000)).size() == 1); + REQUIRE(offset(square, scale_(100000)).size() == 0); } THEN("offset 1000000") { - REQUIRE(offset(square, scale_(1000000)).size() == 1); + REQUIRE(offset(square, scale_(1000000)).size() == 0); } } } diff --git a/tests/libslic3r/test_elephant_foot_compensation.cpp b/tests/libslic3r/test_elephant_foot_compensation.cpp index a95027a3a..3abd3e0fc 100644 --- a/tests/libslic3r/test_elephant_foot_compensation.cpp +++ b/tests/libslic3r/test_elephant_foot_compensation.cpp @@ -432,7 +432,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { ExPolygon expoly = contour_with_hole(); WHEN("Compensated") { // Elephant foot compensation shall not pinch off bits from this contour. - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, 1, false), 0.2f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_with_hole.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -447,7 +447,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Tiny contour") { ExPolygon expoly({ { 133382606, 94912473 }, { 134232493, 95001115 }, { 133783926, 95159440 }, { 133441897, 95180666 }, { 133408242, 95191984 }, { 133339012, 95166830 }, { 132991642, 95011087 }, { 133206549, 94908304 } }); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, 1, false), 0.2f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_tiny.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -462,7 +462,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Large box") { ExPolygon expoly( { {50000000, 50000000 }, { 0, 50000000 }, { 0, 0 }, { 50000000, 0 } } ); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.21f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, 1, false), 0.21f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_large_box.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -477,7 +477,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Thin ring (GH issue #2085)") { ExPolygon expoly = thin_ring(); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, 1, false), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_thin_ring.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -529,7 +529,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { expoly = union_ex({ expoly, expoly2 }).front(); WHEN("Partially compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, 1, false), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_0.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -540,7 +540,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { } } WHEN("Fully compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, false), 0.17f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, 1, false), 0.17f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_1.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -555,7 +555,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Box with hole close to wall (GH issue #2998)") { ExPolygon expoly = box_with_hole_close_to_wall(); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, 1, false), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_2.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -572,7 +572,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { ExPolygon expoly = spirograph_gear_1mm(); WHEN("Partially compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.25f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, 1, false), 0.25f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_2.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -583,7 +583,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { } } WHEN("Fully compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, false), 0.17f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, 1, false), 0.17f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_3.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -594,7 +594,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { } } WHEN("Brutally compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.6f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, 1, false), 0.6f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_4.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, @@ -609,7 +609,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") { GIVEN("Vase with fins") { ExPolygon expoly = vase_with_fins(); WHEN("Compensated") { - ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.41f); + ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, 1, false), 0.41f); #ifdef TESTS_EXPORT_SVGS SVG::export_expolygons(debug_out_path("elephant_foot_compensation_vase_with_fins.svg").c_str(), { { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } }, diff --git a/tests/libslic3r/test_geometry.cpp b/tests/libslic3r/test_geometry.cpp index 886578742..3b8ab4835 100644 --- a/tests/libslic3r/test_geometry.cpp +++ b/tests/libslic3r/test_geometry.cpp @@ -18,7 +18,7 @@ ExtrusionPath* createEP(std::initializer_list vec) { } ExtrusionEntityCollection* createEC(std::initializer_list vec, bool no_sort = false) { ExtrusionEntityCollection *ec = new ExtrusionEntityCollection{}; - ec->no_sort = no_sort; + ec->set_can_sort_reverse(!no_sort, !no_sort); ec->entities = vec; return ec; } @@ -319,7 +319,7 @@ SCENARIO("Path chaining", "[Geometry][!mayfail]") { } } GIVEN("Gyroid infill end points") { - Polylines polylines = { + const Polylines polylines = { { {28122608, 3221037}, {27919139, 56036027} }, { {33642863, 3400772}, {30875220, 56450360} }, { {34579315, 3599827}, {35049758, 55971572} }, @@ -343,16 +343,81 @@ SCENARIO("Path chaining", "[Geometry][!mayfail]") { { {8266122, 14250611}, {6244813, 17751595} }, { {12177955, 9886741}, {10703348, 11491900} } }; - Polylines chained = chain_polylines(polylines); - THEN("Chained taking the shortest path") { - double connection_length = 0.; - for (size_t i = 1; i < chained.size(); ++i) { - const Polyline &pl1 = chained[i - 1]; - const Polyline &pl2 = chained[i]; - connection_length += (pl2.first_point() - pl1.last_point()).cast().norm(); - } - REQUIRE(connection_length < 85206000.); - } + const Polylines chained = chain_polylines(polylines); + THEN("Chained taking the shortest path") { + double connection_length = 0.; + std::cout << "{ {" << chained[0].points.front().x() << ", " << chained[0].points.front().y() << "}, {" << chained[0].points.back().x() << ", " << chained[0].points.back().x() << "} },\n"; + for (size_t i = 1; i < chained.size(); ++i) { + const Polyline& pl1 = chained[i - 1]; + const Polyline& pl2 = chained[i]; + connection_length += (pl2.first_point() - pl1.last_point()).cast().norm(); + std::cout << "{ {" << chained[i].points.front().x() << ", " << chained[i].points.front().y() << "}, {" << chained[i].points.back().x() << ", " << chained[i].points.back().x() << "} },\n"; + } + REQUIRE(connection_length < 85206000.); + } + const ExtrusionPath pattern(ExtrusionRole::erPerimeter); + THEN("Chained taking the shortest path with extrusionpaths") { + ExtrusionEntityCollection coll; + for (auto poly : polylines) + coll.entities.push_back(new ExtrusionPath(poly, pattern)); + chain_and_reorder_extrusion_entities(coll.entities, &polylines[18].points.back()); + double connection_length = 0.; + std::cout << "{ {" << coll.entities[0]->as_polyline().points.front().x() << ", " << coll.entities[0]->as_polyline().points.front().y() << "}, {" << coll.entities[0]->as_polyline().points.back().x() << ", " << coll.entities[0]->as_polyline().points.back().y() << "} },\n"; + for (size_t i = 1; i < coll.entities.size(); ++i) { + const Polyline& pl1 = coll.entities[i - 1]->as_polyline(); + const Polyline& pl2 = coll.entities[i]->as_polyline(); + connection_length += (pl2.first_point() - pl1.last_point()).cast().norm(); + std::cout << "{ {" << coll.entities[i]->as_polyline().points.front().x() << ", " << coll.entities[i]->as_polyline().points.front().y() << "}, {" << coll.entities[i]->as_polyline().points.back().x() << ", " << coll.entities[i]->as_polyline().points.back().y() << "} },\n"; + } + REQUIRE(connection_length < 85206000.); + } + THEN("Chained can't unfold a eeCollection") { + ExtrusionEntityCollection coll; + for (auto poly : polylines) + coll.entities.push_back(new ExtrusionPath(poly, pattern)); + ExtrusionEntitiesPtr data{ &coll }; + chain_and_reorder_extrusion_entities(data, &polylines[18].points.back()); + double connection_length = 0.; + for (size_t i = 1; i < coll.entities.size(); ++i) { + const Polyline& pl1 = coll.entities[i - 1]->as_polyline(); + const Polyline& pl2 = coll.entities[i]->as_polyline(); + connection_length += (pl2.first_point() - pl1.last_point()).cast().norm(); + } + REQUIRE(connection_length > 85206000.); + REQUIRE(polylines[18].points.front() != coll.entities[18]->first_point()); + } + THEN("Chained does not take the shortest path with extrusionpaths if in an un-sortable un-reversable collection") { + ExtrusionEntityCollection coll; + for (auto poly : polylines) + coll.entities.push_back(new ExtrusionPath(poly, pattern)); + ExtrusionEntitiesPtr data{ &coll }; + coll.set_can_sort_reverse(false, false); + chain_and_reorder_extrusion_entities(data, &polylines[18].points.back()); + double connection_length = 0.; + for (size_t i = 1; i < coll.entities.size(); ++i) { + const Polyline& pl1 = coll.entities[i - 1]->as_polyline(); + const Polyline& pl2 = coll.entities[i]->as_polyline(); + connection_length += (pl2.first_point() - pl1.last_point()).cast().norm(); + } + REQUIRE(connection_length > 85206000.); + REQUIRE(polylines[18].points.front() == coll.entities[18]->first_point()); + } + THEN("Chained does not take the shortest path with extrusionpaths if in an un-sortable collection") { + ExtrusionEntityCollection coll; + for (auto poly : polylines) + coll.entities.push_back(new ExtrusionPath(poly, pattern)); + ExtrusionEntitiesPtr data{ &coll }; + coll.set_can_sort_reverse(false, true); + chain_and_reorder_extrusion_entities(data, &polylines[18].points.back()); + double connection_length = 0.; + for (size_t i = 1; i < coll.entities.size(); ++i) { + const Polyline& pl1 = coll.entities[i - 1]->as_polyline(); + const Polyline& pl2 = coll.entities[i]->as_polyline(); + connection_length += (pl2.first_point() - pl1.last_point()).cast().norm(); + } + REQUIRE(connection_length > 85206000.); + REQUIRE(polylines[18].points.front() != coll.entities[18]->first_point()); + } } GIVEN("Loop pieces") { Point a { 2185796, 19058485 }; -- cgit v1.2.3 From b2669f711f781665f224c80dd7ac1414ff5e5997 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 11 Dec 2021 18:34:18 +0100 Subject: Fix wrong bridge offset (can lead to no bridge) --- src/libslic3r/Fill/Fill.cpp | 8 ++++---- src/libslic3r/Fill/FillRectilinear.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 93360eb75..4d1b237b2 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -508,7 +508,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: //adjust the bridge density if (surface_fill.params.flow.bridge && surface_fill.params.density > 0.99 /*&& layerm->region()->config().bridge_overlap.get_abs_value(1) != 1*/) { - ////varies the overlap to have teh best coverage for the bridge + ////varies the overlap to have the best coverage for the bridge //surface_fill.params.density *= float(layerm->region()->config().bridge_overlap.get_abs_value(1)); double min_spacing = 0.999 * surface_fill.params.spacing / surface_fill.params.config->bridge_overlap.get_abs_value(surface_fill.params.density); double max_spacing = 1.001 * surface_fill.params.spacing / surface_fill.params.config->bridge_overlap_min.get_abs_value(surface_fill.params.density); @@ -516,9 +516,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: if (min_spacing < max_spacing * 1.01) { // create a bouding box of the rotated surface coord_t bounding_box_size_x = 0; - Polygon poly = surface_fill.surface.expolygon.contour; coord_t bounding_box_min_x = 0; - poly.rotate(PI / 2 - (surface_fill.params.bridge_angle < 0 ? surface_fill.params.angle : surface_fill.params.bridge_angle)); ExPolygons expolys; if (surface_fill.params.bridge_angle > 0 && !f->no_overlap_expolygons.empty()) { //take only the no-overlap area @@ -530,7 +528,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: bool first = true; for (ExPolygon& expoly : expolys) { expoly.holes.clear(); - expoly.rotate(PI / 2 - (surface_fill.params.bridge_angle < 0 ? surface_fill.params.angle : surface_fill.params.bridge_angle)); + expoly.rotate(PI / 2 + (surface_fill.params.bridge_angle < 0 ? surface_fill.params.angle : surface_fill.params.bridge_angle)); if (first) { bb = expoly.contour.bounding_box(); first = false; @@ -558,6 +556,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: surface_fill.params.density = surface_fill.params.config->bridge_overlap.get_abs_value(surface_fill.params.density); } } + Polygon poly = surface_fill.surface.expolygon.contour; + poly.rotate(PI / 2 + (surface_fill.params.bridge_angle < 0 ? surface_fill.params.angle : surface_fill.params.bridge_angle)); surface_fill.params.dont_adjust = true; surface_fill.params.bridge_offset = std::abs(poly.bounding_box().min.x() - bounding_box_min_x); } diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 6495dae72..fd53d96a8 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -777,7 +777,7 @@ std::vector FillRectilinear::_vert_lines_for_polygon( size_t n_vlines = 1 + (bounding_box.max.x() - bounding_box.min.x() - 10) / line_spacing; coord_t x0 = bounding_box.min.x(); if (params.flow.bridge && params.bridge_offset >= 0) { - x0 += params.bridge_offset; + x0 += params.bridge_offset; }else if (params.full_infill()) x0 += (line_spacing + coord_t(SCALED_EPSILON)) / 2; -- cgit v1.2.3 From 4f5e801ecf98f8ab7d2600e2a36d54935c6fe079 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 11 Dec 2021 19:34:58 +0100 Subject: fix some seam_gap issues: * fix scale * continue traveling up to the old gap position (or continue with wipe_extra_perimeter) supermerill/SuperSlicer#57 --- src/libslic3r/ExtrusionEntity.cpp | 14 ++++++++----- src/libslic3r/ExtrusionEntity.hpp | 2 +- src/libslic3r/GCode.cpp | 41 +++++++++++++++++++++++++++++---------- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 5ed878499..250359aef 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -213,21 +213,25 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) this->split_at_vertex(p); } -void ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const +ExtrusionPaths clip_end(ExtrusionPaths& paths, double distance) { - *paths = this->paths; + ExtrusionPaths removed; - while (distance > 0 && !paths->empty()) { - ExtrusionPath &last = paths->back(); + while (distance > 0 && !paths.empty()) { + ExtrusionPath& last = paths.back(); + removed.push_back(last); double len = last.length(); if (len <= distance) { - paths->pop_back(); + paths.pop_back(); distance -= len; } else { last.polyline.clip_end(distance); + removed.back().polyline.clip_start(removed.back().polyline.length() - distance); break; } } + std::reverse(removed.begin(), removed.end()); + return removed; } bool ExtrusionLoop::has_overhang_point(const Point &point) const diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 5369998cf..36198f125 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -279,6 +279,7 @@ protected: ExtrusionRole m_role; }; typedef std::vector ExtrusionPaths; +ExtrusionPaths clip_end(ExtrusionPaths& paths, double distance); class ExtrusionPath3D : public ExtrusionPath { public: @@ -451,7 +452,6 @@ public: double length() const override; bool split_at_vertex(const Point &point); void split_at(const Point &point, bool prefer_non_overhang); - void clip_end(double distance, ExtrusionPaths* paths) const; // Test, whether the point is extruded by a bridging flow. // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead. bool has_overhang_point(const Point &point) const; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index edf1677b2..b4f546bf7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2949,13 +2949,23 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s // clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case - double clip_length = 0; + coordf_t clip_length = 0; + coordf_t min_clip_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)) * 0.15; if (m_enable_loop_clipping && m_writer.tool_is_extruder()) - clip_length = m_config.seam_gap.get_abs_value(m_writer.tool()->id(), scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + clip_length = scale_(m_config.seam_gap.get_abs_value(m_writer.tool()->id(), EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); // get paths - ExtrusionPaths paths; - loop.clip_end(clip_length, &paths); + ExtrusionPaths paths = loop.paths; + ExtrusionPaths clipped; + if (clip_length > min_clip_length) { + clipped = clip_end(paths, clip_length); + clip_end(clipped, min_clip_length); + for (ExtrusionPath& ep : clipped) + ep.mm3_per_mm = 0; + append(paths, clipped); + } else { + clip_end(paths, clip_length); + } if (paths.empty()) return ""; // apply the small/external? perimeter speed @@ -3063,7 +3073,9 @@ std::string GCode::extrude_loop_vase(const ExtrusionLoop &original_loop, const s double e_per_mm_per_height = (path->mm3_per_mm / this->m_layer->height) * m_writer.tool()->e_per_mm3() * this->config().print_extrusion_multiplier.get_abs_value(1); - if (m_writer.extrusion_axis().empty()) e_per_mm_per_height = 0; + if (m_writer.extrusion_axis().empty()) + e_per_mm_per_height = 0; + //extrude { std::string comment = m_config.gcode_comments ? description : ""; for (const Line &line : path->polyline.lines()) { @@ -3271,13 +3283,23 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s // clip the path to avoid the extruder to get exactly on the first point of the loop; // if polyline was shorter than the clipping distance we'd get a null polyline, so // we discard it in that case - double clip_length = 0; + coordf_t clip_length = 0; + coordf_t min_clip_length = scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0)) * 0.15; if (m_enable_loop_clipping && m_writer.tool_is_extruder()) - clip_length = m_config.seam_gap.get_abs_value(m_writer.tool()->id(), scale_(EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); + clip_length = scale_(m_config.seam_gap.get_abs_value(m_writer.tool()->id(), EXTRUDER_CONFIG_WITH_DEFAULT(nozzle_diameter, 0))); // get paths - ExtrusionPaths paths; - loop.clip_end(clip_length, &paths); + ExtrusionPaths paths = loop.paths; + ExtrusionPaths clipped; + if (clip_length > min_clip_length) { + clipped = clip_end(paths, clip_length); + clip_end(clipped, min_clip_length); + for (ExtrusionPath& ep : clipped) + ep.mm3_per_mm = 0; + append(paths, clipped); + } else { + clip_end(paths, clip_length); + } if (paths.empty()) return ""; // apply the small perimeter speed @@ -3298,7 +3320,6 @@ std::string GCode::extrude_loop(const ExtrusionLoop &original_loop, const std::s std::string gcode; for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { //path->simplify(SCALED_RESOLUTION); //should already be simplified - //gcode += this->_extrude(*path, description, speed); if(path->polyline.points.size()>1) gcode += extrude_path(*path, description, speed); } -- cgit v1.2.3 From 5a5ea52a6ed6f6a4530c4514b33eb6fe43ac8021 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 11 Dec 2021 21:01:59 +0100 Subject: fix disjointed fill surface also gapfill "star" are now even more restricted in min branch length. supermerill/SuperSlicer#2038 --- src/libslic3r/Fill/Fill.cpp | 2 ++ src/libslic3r/MedialAxis.cpp | 76 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index 4d1b237b2..0639f95ab 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -486,6 +486,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: surface_fill.params.flow = Flow::new_from_spacing((float)f->get_spacing(), surface_fill.params.flow.nozzle_diameter, (float)surface_fill.params.flow.height, overlap, surface_fill.params.flow.bridge); } + //union with safety offset to avoid separation from the appends of different surface with same settings. + surface_fill.expolygons = union_ex(surface_fill.expolygons, true); for (ExPolygon &expoly : surface_fill.expolygons) { //set overlap polygons diff --git a/src/libslic3r/MedialAxis.cpp b/src/libslic3r/MedialAxis.cpp index a84b69204..c44c1d97a 100644 --- a/src/libslic3r/MedialAxis.cpp +++ b/src/libslic3r/MedialAxis.cpp @@ -1395,6 +1395,65 @@ MedialAxis::remove_too_thin_points(ThickPolylines& pp) void MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_size) { + // reduce the flow at the intersection ( + ) points + //FIXME: TODO: note that crossings are unnafected right now. they may need a different codepath directly in their method + //TODO: unit tests for that. + //TODO: never triggered. ther's only the sections passed by crossing fusion that aren't edge-case and it's not treated by this. => comment for now + //for each not-endpoint point + //std::vector endpoint_not_used(pp.size() * 2, true); + //for (size_t idx_endpoint = 0; idx_endpoint < endpoint_not_used.size(); idx_endpoint++) { + // ThickPolyline& polyline = pp[idx_endpoint / 2]; + // //update endpoint_not_used if not seen before + // if (idx_endpoint % 2 == 0 && endpoint_not_used[idx_endpoint]) { + // //update + // endpoint_not_used[(idx_endpoint / 2)] = !polyline.endpoints.first; + // endpoint_not_used[(idx_endpoint / 2) + 1] = endpoint_not_used[(idx_endpoint / 2) + 1] && !polyline.endpoints.second; + // } + // if (endpoint_not_used[idx_endpoint]) { + // int nb_endpoints; + // Point pt = idx_endpoint % 2 == 0 ? polyline.first_point() : polyline.last_point(); + // if (idx_endpoint % 2 == 0 && pt.coincides_with(polyline.last_point())) { + // nb_endpoints++; + // endpoint_not_used[(idx_endpoint / 2) + 1] = false; + // } + // //good, now find other points + // for (size_t idx_other_pp = (idx_endpoint / 2) + 1; idx_other_pp < pp.size(); idx_other_pp++) { + // ThickPolyline& other = pp[idx_other_pp]; + // if (pt.coincides_with(other.first_point())) { + // nb_endpoints++; + // endpoint_not_used[idx_other_pp * 2] = false; + // } + // if (pt.coincides_with(other.last_point())) { + // nb_endpoints++; + // endpoint_not_used[idx_other_pp * 2 + 1] = false; + // } + // } + // if (nb_endpoints < 3) + // continue; + // // reduce width accordingly + // float reduction = 2.f / nb_endpoints; + // std::cout << "reduce " << reduction << " points!\n"; + // if (idx_endpoint % 2 == 0 ) { + // polyline.width.front() *= reduction; + // if(pt.coincides_with(polyline.last_point())) + // polyline.width.back() *= reduction; + // } else { + // polyline.width.back() *= reduction; + // } + // //good, now find other points + // for (size_t idx_other_pp = (idx_endpoint / 2) + 1; idx_other_pp < pp.size(); idx_other_pp++) { + // ThickPolyline& other = pp[idx_other_pp]; + // if (pt.coincides_with(other.first_point())) { + // other.width.front() *= reduction; + // } + // if (pt.coincides_with(other.last_point())) { + // other.width.back() *= reduction; + // } + // } + // //TODO: restore good width at width dist, or reduce other points up to width dist + // } + //} + //remove too short polyline bool changes = true; while (changes) { @@ -1408,11 +1467,16 @@ MedialAxis::remove_too_short_polylines(ThickPolylines& pp, const coord_t min_siz // (we can't do this check before endpoints extension and clipping because we don't // know how long will the endpoints be extended since it depends on polygon thickness // which is variable - extension will be <= max_width/2 on each side) - if ((polyline.endpoints.first || polyline.endpoints.second) - && polyline.length() < max_width / 2) { - if (shortest_size > polyline.length()) { - shortest_size = polyline.length(); - shortest_idx = i; + if ((polyline.endpoints.first || polyline.endpoints.second)) { + coordf_t max_width = max_width / 2; + for (coordf_t w : polyline.width) + max_width = std::max(max_width, w); + if(polyline.length() < max_width) { + if (shortest_size > polyline.length()) { + shortest_size = polyline.length(); + shortest_idx = i; + } + } } @@ -1857,6 +1921,7 @@ MedialAxis::build(ThickPolylines &polylines_out) // svg.draw(pp); // svg.Close(); //} + //TODO: reduce the flow at the intersection ( + ) points on crossing? concatenate_polylines_with_crossing(pp); //{ // std::stringstream stri; @@ -1879,7 +1944,6 @@ MedialAxis::build(ThickPolylines &polylines_out) // svg.Close(); //} - //TODO: reduce the flow at the intersection ( + ) points ? ensure_not_overextrude(pp); //{ // std::stringstream stri; -- cgit v1.2.3 From b1e044855e1f38a5b177ce9b58d3d09cef89c3f1 Mon Sep 17 00:00:00 2001 From: supermerill Date: Sat, 11 Dec 2021 21:41:40 +0100 Subject: Don't consider ' ' or '\n' for custom toolchange gcode detection supermerill/SuperSlicer#1368 --- src/libslic3r/GCode.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index b4f546bf7..cd4a79af0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4401,7 +4401,8 @@ std::string GCode::toolchange(uint16_t extruder_id, double print_z) { std::string gcode; // Process the custom toolchange_gcode. If it is empty, insert just a Tn command. - const std::string& toolchange_gcode = m_config.toolchange_gcode.value; + std::string toolchange_gcode = m_config.toolchange_gcode.value; + boost::trim(toolchange_gcode); //remove invisible characters that may compromize the 'toolchange_gcode.empty()' std::string toolchange_gcode_parsed; if (!toolchange_gcode.empty() && m_writer.multiple_extruders) { DynamicConfig config; -- cgit v1.2.3