From 59502e7861d43fba99cf08ff51fe5529b8e253ef Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 12 Oct 2021 14:47:23 +0200 Subject: [PATCH 1/3] Seam placement improvements --- src/libslic3r/ExtrusionEntity.cpp | 24 ++-- src/libslic3r/ExtrusionEntity.hpp | 1 + src/libslic3r/GCode.cpp | 27 ++--- src/libslic3r/GCode/SeamPlacer.cpp | 189 +++++++++++++++++++++++------ src/libslic3r/GCode/SeamPlacer.hpp | 31 ++++- src/libslic3r/MultiPoint.cpp | 4 +- 6 files changed, 203 insertions(+), 73 deletions(-) diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index d9320373f..7b2506a22 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -193,12 +193,8 @@ bool ExtrusionLoop::split_at_vertex(const Point &point) return false; } -// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging. -void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) +std::pair ExtrusionLoop::get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const { - if (this->paths.empty()) - return; - // Find the closest path and closest point belonging to that path. Avoid overhangs, if asked for. size_t path_idx = 0; Point p; @@ -207,15 +203,15 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) Point p_non_overhang; size_t path_idx_non_overhang = 0; double min_non_overhang = std::numeric_limits::max(); - for (const ExtrusionPath &path : this->paths) { + for (const ExtrusionPath& path : this->paths) { Point p_tmp = point.projection_onto(path.polyline); double dist = (p_tmp - point).cast().norm(); if (dist < min) { p = p_tmp; min = dist; path_idx = &path - &this->paths.front(); - } - if (prefer_non_overhang && ! is_bridge(path.role()) && dist < min_non_overhang) { + } + if (prefer_non_overhang && !is_bridge(path.role()) && dist < min_non_overhang) { p_non_overhang = p_tmp; min_non_overhang = dist; path_idx_non_overhang = &path - &this->paths.front(); @@ -224,9 +220,19 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) if (prefer_non_overhang && min_non_overhang != std::numeric_limits::max()) { // Only apply the non-overhang point if there is one. path_idx = path_idx_non_overhang; - p = p_non_overhang; + p = p_non_overhang; } } + return std::make_pair(path_idx, p); +} + +// Splitting an extrusion loop, possibly made of multiple segments, some of the segments may be bridging. +void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) +{ + if (this->paths.empty()) + return; + + auto [path_idx, p] = get_closest_path_and_point(point, prefer_non_overhang); // now split path_idx in two parts const ExtrusionPath &path = this->paths[path_idx]; diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 2f3508316..1c990f5ea 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -258,6 +258,7 @@ public: double length() const override; bool split_at_vertex(const Point &point); void split_at(const Point &point, bool prefer_non_overhang); + std::pair get_closest_path_and_point(const Point& point, bool prefer_non_overhang) const; 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. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 116fcbe7f..137ae1847 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2508,28 +2508,14 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // extrude all loops ccw bool was_clockwise = loop.make_counter_clockwise(); - SeamPosition seam_position = m_config.seam_position; - if (loop.loop_role() == elrSkirt) - seam_position = spNearest; - // find the point of the loop that is closest to the current extruder position // or randomize if requested Point last_pos = this->last_pos(); if (m_config.spiral_vase) { loop.split_at(last_pos, false); - } else { - const EdgeGrid::Grid* edge_grid_ptr = (lower_layer_edge_grid && *lower_layer_edge_grid) - ? lower_layer_edge_grid->get() - : nullptr; - Point seam = m_seam_placer.get_seam(*m_layer, seam_position, loop, - last_pos, EXTRUDER_CONFIG(nozzle_diameter), - (m_layer == NULL ? nullptr : m_layer->object()), - was_clockwise, edge_grid_ptr); - // Split the loop at the point with a minium penalty. - if (!loop.split_at_vertex(seam)) - // The point is not in the original loop. Insert it. - loop.split_at(seam, true); } + else + m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first, EXTRUDER_CONFIG(nozzle_diameter)); // 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 @@ -2652,7 +2638,14 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector(region.perimeters.begin(), region.perimeters.end()), + *m_layer, m_config.seam_position, + m_config.external_perimeters_first, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), + (m_layer == NULL ? nullptr : m_layer->object()), + (lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr)); + + for (const ExtrusionEntity* ee : region.perimeters) gcode += this->extrude_entity(*ee, "perimeter", -1., &lower_layer_edge_grid); } return gcode; diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 878a9ef58..609ad4d2d 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -292,11 +292,154 @@ void SeamPlacer::init(const Print& print) -Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position, - const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr, - const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid) +void SeamPlacer::plan_perimeters(const std::vector perimeters, + const Layer& layer, SeamPosition seam_position, bool external_first, + Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid) { + // When printing the perimeters, we want the seams on external and internal perimeters to match. + // We have a list of perimeters in the order to be printed. Each internal perimeter must inherit + // the seam from the previous external perimeter. + + m_plan.clear(); + m_plan_idx = 0; + + if (perimeters.empty() || ! po) + return; + + m_plan.resize(perimeters.size()); + + for (int i = 0; i < int(perimeters.size()); ++i) { + if (perimeters[i]->role() == erExternalPerimeter && perimeters[i]->is_loop()) { + last_pos = this->calculate_seam( + layer, seam_position, *dynamic_cast(perimeters[i]), nozzle_dmr, + po, lower_layer_edge_grid, last_pos); + m_plan[i].external = true; + } + m_plan[i].pt = last_pos; + } +} + + +void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter) +{ + const double seam_offset = nozzle_diameter; + + Point seam = last_pos; + if (! m_plan.empty() && m_plan_idx < m_plan.size()) { + if (m_plan[m_plan_idx].external) + seam = m_plan[m_plan_idx].pt; + else if (! external_first) { + // Internal perimeter printed before the external. + // First get list of external seams. + std::vector ext_seams; + for (size_t i = 0; i < m_plan.size(); ++i) { + if (m_plan[i].external) + ext_seams.emplace_back(i); + } + + if (! ext_seams.empty()) { + // First find the line segment closest to an external seam: + int path_idx = 0; + int line_idx = 0; + size_t ext_seam_idx = -1; + double min_dist_sqr = std::numeric_limits::max(); + double nozzle_diameter_sqr = std::pow(scale_(nozzle_diameter), 2.); + std::vector lines_vect; + for (int i = 0; i < loop.paths.size(); ++i) { + lines_vect.emplace_back(loop.paths[i].polyline.lines()); + const Lines& lines = lines_vect.back(); + for (int j = 0; j < lines.size(); ++j) { + for (size_t k : ext_seams) { + double d_sqr = lines[j].distance_to_squared(m_plan[k].pt); + if (d_sqr < min_dist_sqr) { + path_idx = i; + line_idx = j; + ext_seam_idx = k; + min_dist_sqr = d_sqr; + } + } + } + } + + // Only accept seam that is reasonably close. + double limit_dist_sqr = std::pow(double(scale_((ext_seam_idx - m_plan_idx) * nozzle_diameter * 2.)), 2.); + if (ext_seam_idx != -1 && min_dist_sqr < limit_dist_sqr) { + // Now find a projection of the external seam + const Lines& lines = lines_vect[path_idx]; + Point closest = m_plan[ext_seam_idx].pt.projection_onto(lines[line_idx]); + double dist = (closest.cast() - lines[line_idx].b.cast()).norm(); + + // And walk along the perimeter until we make enough space for + // seams of all perimeters beforethe external one. + double offset = (ext_seam_idx - m_plan_idx) * scale_(seam_offset); + double last_offset = offset; + offset -= dist; + const Point* a = &closest; + const Point* b = &lines[line_idx].b; + while (++line_idx < lines.size() && offset > 0.) { + last_offset = offset; + offset -= lines[line_idx].length(); + a = &lines[line_idx].a; + b = &lines[line_idx].b; + } + + // We have walked far enough, too far maybe. Interpolate on the + // last segment to find the end precisely. + offset = std::min(0., offset); // In case that offset is still positive (we may have "wrapped around") + double ratio = last_offset / (last_offset - offset); + seam = (a->cast() + ((b->cast() - a->cast()) * ratio)).cast(); + } + } + } + else { + // We should have a candidate ready from before. If not, use last_pos. + if (m_plan_idx > 0 && m_plan[m_plan_idx - 1].precalculated) + seam = m_plan[m_plan_idx - 1].pt; + } + } + + + // Split the loop at the point with a minium penalty. + if (!loop.split_at_vertex(seam)) + // The point is not in the original loop. Insert it. + loop.split_at(seam, true); + + if (external_first && m_plan_idx+1 1) { + const ExtrusionPath& last = loop.paths.back(); + auto it = last.polyline.points.crbegin() + 1; + for (; it != last.polyline.points.crend(); ++it) { + running_sqr += (it->cast() - (it - 1)->cast()).squaredNorm(); + if (running_sqr > dist_sqr) + break; + running_sqr_last = running_sqr; + } + if (running_sqr <= dist_sqr) + it = last.polyline.points.crend() - 1; + // Now interpolate. + double ratio = (std::sqrt(dist_sqr) - std::sqrt(running_sqr_last)) / (std::sqrt(running_sqr) - std::sqrt(running_sqr_last)); + m_plan[m_plan_idx + 1].pt = ((it - 1)->cast() + (it->cast() - (it - 1)->cast()) * std::min(ratio, 1.)).cast(); + m_plan[m_plan_idx + 1].precalculated = true; + } + } + + ++m_plan_idx; +} + + +// Returns a seam for an EXTERNAL perimeter. +Point SeamPlacer::calculate_seam(const Layer& layer, const SeamPosition seam_position, + const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos) +{ + assert(loop.role() == erExternalPerimeter); Polygon polygon = loop.polygon(); + bool was_clockwise = polygon.make_counter_clockwise(); BoundingBox polygon_bb = polygon.bounding_box(); const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5); @@ -438,7 +581,7 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position, } } - if (seam_position == spAligned && loop.role() == erExternalPerimeter) + if (seam_position == spAligned) m_seam_history.add_seam(po, polygon.points[idx_min], polygon_bb); @@ -466,42 +609,8 @@ Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position, #endif return polygon.points[idx_min]; - } else { // spRandom - if (po->print()->default_region_config().external_perimeters_first) { - if (loop.role() == erExternalPerimeter) - last_pos = this->get_random_seam(layer_idx, polygon, po_idx); - else { - // Internal perimeters will just use last_pos. - } - } else { - if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) { - // This loop does not contain any other loop. Set a random position. - // The other loops will get a seam close to the random point chosen - // on the innermost contour. - last_pos = this->get_random_seam(layer_idx, polygon, po_idx); - m_last_loop_was_external = false; - } - if (loop.role() == erExternalPerimeter) { - if (m_last_loop_was_external) { - // There was no internal perimeter before this one. - last_pos = this->get_random_seam(layer_idx, polygon, po_idx); - } else { - if (is_custom_seam_on_layer(layer_idx, po_idx)) { - // There is a possibility that the loop will be influenced by custom - // seam enforcer/blocker. In this case do not inherit the seam - // from internal loops (which may conflict with the custom selection - // and generate another random one. - bool saw_custom = false; - Point candidate = this->get_random_seam(layer_idx, polygon, po_idx, &saw_custom); - if (saw_custom) - last_pos = candidate; - } - } - m_last_loop_was_external = true; - } - } - return last_pos; - } + } else + return this->get_random_seam(layer_idx, polygon, po_idx); } diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 0bae7af5a..5545fcdf9 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -2,7 +2,9 @@ #define libslic3r_SeamPlacer_hpp_ #include +#include +#include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/BoundingBox.hpp" @@ -41,16 +43,29 @@ class SeamPlacer { public: void init(const Print& print); - Point get_seam(const Layer& layer, const SeamPosition seam_position, - const ExtrusionLoop& loop, Point last_pos, - coordf_t nozzle_diameter, const PrintObject* po, - bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid); + // When perimeters are printed, first call this function with the respective + // external perimeter. SeamPlacer will find a location for its seam and remember it. + // Subsequent calls to get_seam will return this position. + + + void plan_perimeters(const std::vector perimeters, + const Layer& layer, SeamPosition seam_position, bool external_first, + Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid); + + void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter); + using TreeType = AABBTreeIndirect::Tree<2, coord_t>; using AlignedBoxType = Eigen::AlignedBox; private: + // When given an external perimeter (!), returns the seam. + Point calculate_seam(const Layer& layer, const SeamPosition seam_position, + const ExtrusionLoop& loop, coordf_t nozzle_dmr, const PrintObject* po, + const EdgeGrid::Grid* lower_layer_edge_grid, Point last_pos); + struct CustomTrianglesPerLayer { Polygons polys; TreeType tree; @@ -61,7 +76,13 @@ private: coordf_t m_last_print_z = -1.; const PrintObject* m_last_po = nullptr; - bool m_last_loop_was_external = true; + struct SeamPoint { + Point pt; + bool precalculated = false; + bool external = false; + }; + std::vector m_plan; + size_t m_plan_idx; std::vector> m_enforcers; std::vector> m_blockers; diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 03f7ff59c..aa8295098 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -47,9 +47,9 @@ void MultiPoint::rotate(double angle, const Point ¢er) double MultiPoint::length() const { - Lines lines = this->lines(); + const Lines& lines = this->lines(); double len = 0; - for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) { + for (auto it = lines.cbegin(); it != lines.cend(); ++it) { len += it->length(); } return len; From 19eb984d72a27e4b3bd2147f43e3f3e792a4aeae Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 19 Oct 2021 12:05:23 +0200 Subject: [PATCH 2/3] SeamPlacer: one more heuristic to get rid of long travels --- src/libslic3r/GCode.cpp | 6 +++--- src/libslic3r/GCode/SeamPlacer.cpp | 29 ++++++++++++++++++++--------- src/libslic3r/GCode/SeamPlacer.hpp | 8 ++++++-- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 137ae1847..97599ca07 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2515,7 +2515,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou loop.split_at(last_pos, false); } else - m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first, EXTRUDER_CONFIG(nozzle_diameter)); + m_seam_placer.place_seam(loop, this->last_pos(), m_config.external_perimeters_first, + EXTRUDER_CONFIG(nozzle_diameter), lower_layer_edge_grid ? lower_layer_edge_grid->get() : nullptr); // 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 @@ -2640,8 +2641,7 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector(region.perimeters.begin(), region.perimeters.end()), - *m_layer, m_config.seam_position, - m_config.external_perimeters_first, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), + *m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), (m_layer == NULL ? nullptr : m_layer->object()), (lower_layer_edge_grid ? lower_layer_edge_grid.get() : nullptr)); diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 609ad4d2d..6d082a431 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -293,7 +293,7 @@ void SeamPlacer::init(const Print& print) void SeamPlacer::plan_perimeters(const std::vector perimeters, - const Layer& layer, SeamPosition seam_position, bool external_first, + const Layer& layer, SeamPosition seam_position, Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, const EdgeGrid::Grid* lower_layer_edge_grid) { @@ -315,20 +315,32 @@ void SeamPlacer::plan_perimeters(const std::vector perim layer, seam_position, *dynamic_cast(perimeters[i]), nozzle_dmr, po, lower_layer_edge_grid, last_pos); m_plan[i].external = true; + m_plan[i].seam_position = seam_position; + m_plan[i].layer = &layer; + m_plan[i].po = po; } m_plan[i].pt = last_pos; } } -void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter) +void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter, + const EdgeGrid::Grid* lower_layer_edge_grid) { const double seam_offset = nozzle_diameter; Point seam = last_pos; if (! m_plan.empty() && m_plan_idx < m_plan.size()) { - if (m_plan[m_plan_idx].external) + if (m_plan[m_plan_idx].external) { seam = m_plan[m_plan_idx].pt; + // One more heuristics: if the seam is too far from current nozzle position, + // try to place it again. This can happen in cases where the external perimeter + // does not belong to the preceding ones and they are ordered so they end up + // far from each other. + if ((seam.cast() - last_pos.cast()).squaredNorm() > std::pow(scale_(5.*nozzle_diameter), 2.)) + seam = this->calculate_seam(*m_plan[m_plan_idx].layer, m_plan[m_plan_idx].seam_position, loop, nozzle_diameter, + m_plan[m_plan_idx].po, lower_layer_edge_grid, last_pos); + } else if (! external_first) { // Internal perimeter printed before the external. // First get list of external seams. @@ -342,14 +354,13 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext // First find the line segment closest to an external seam: int path_idx = 0; int line_idx = 0; - size_t ext_seam_idx = -1; + size_t ext_seam_idx = size_t(-1); double min_dist_sqr = std::numeric_limits::max(); - double nozzle_diameter_sqr = std::pow(scale_(nozzle_diameter), 2.); std::vector lines_vect; - for (int i = 0; i < loop.paths.size(); ++i) { + for (int i = 0; i < int(loop.paths.size()); ++i) { lines_vect.emplace_back(loop.paths[i].polyline.lines()); const Lines& lines = lines_vect.back(); - for (int j = 0; j < lines.size(); ++j) { + for (int j = 0; j < int(lines.size()); ++j) { for (size_t k : ext_seams) { double d_sqr = lines[j].distance_to_squared(m_plan[k].pt); if (d_sqr < min_dist_sqr) { @@ -364,7 +375,7 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext // Only accept seam that is reasonably close. double limit_dist_sqr = std::pow(double(scale_((ext_seam_idx - m_plan_idx) * nozzle_diameter * 2.)), 2.); - if (ext_seam_idx != -1 && min_dist_sqr < limit_dist_sqr) { + if (ext_seam_idx != size_t(-1) && min_dist_sqr < limit_dist_sqr) { // Now find a projection of the external seam const Lines& lines = lines_vect[path_idx]; Point closest = m_plan[ext_seam_idx].pt.projection_onto(lines[line_idx]); @@ -377,7 +388,7 @@ void SeamPlacer::place_seam(ExtrusionLoop& loop, const Point& last_pos, bool ext offset -= dist; const Point* a = &closest; const Point* b = &lines[line_idx].b; - while (++line_idx < lines.size() && offset > 0.) { + while (++line_idx < int(lines.size()) && offset > 0.) { last_offset = offset; offset -= lines[line_idx].length(); a = &lines[line_idx].a; diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 5545fcdf9..57c3532c3 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -49,11 +49,12 @@ public: void plan_perimeters(const std::vector perimeters, - const Layer& layer, SeamPosition seam_position, bool external_first, + const Layer& layer, SeamPosition seam_position, Point last_pos, coordf_t nozzle_dmr, const PrintObject* po, const EdgeGrid::Grid* lower_layer_edge_grid); - void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter); + void place_seam(ExtrusionLoop& loop, const Point& last_pos, bool external_first, double nozzle_diameter, + const EdgeGrid::Grid* lower_layer_edge_grid); using TreeType = AABBTreeIndirect::Tree<2, coord_t>; @@ -80,6 +81,9 @@ private: Point pt; bool precalculated = false; bool external = false; + const Layer* layer = nullptr; + SeamPosition seam_position; + const PrintObject* po = nullptr; }; std::vector m_plan; size_t m_plan_idx; From 00dfb8f69cb1dc565b53f6a21be2d28f9fb9b8b9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 22 Oct 2021 13:15:42 +0200 Subject: [PATCH 3/3] Make sure that we have the lower_layer_edge_grid when placing seams --- src/libslic3r/GCode.cpp | 49 ++++++++++++++++++++++---------------- src/slic3r/GUI/GUI_App.cpp | 1 + 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 97599ca07..8f8d8dbe0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2479,31 +2479,36 @@ std::string GCode::change_layer(coordf_t print_z) +static std::unique_ptr calculate_layer_edge_grid(const Layer& layer) +{ + auto out = make_unique(); + + // Create the distance field for a layer below. + const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5); + out->create(layer.lslices, distance_field_resolution); + out->calculate_sdf(); +#if 0 + { + static int iRun = 0; + BoundingBox bbox = (*lower_layer_edge_grid)->bbox(); + bbox.min(0) -= scale_(5.f); + bbox.min(1) -= scale_(5.f); + bbox.max(0) += scale_(5.f); + bbox.max(1) += scale_(5.f); + EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++)); + } +#endif + return out; +} + + std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, double speed, std::unique_ptr *lower_layer_edge_grid) { // get a copy; don't modify the orientation of the original loop object otherwise // next copies (if any) would not detect the correct orientation - if (m_layer->lower_layer != nullptr && lower_layer_edge_grid != nullptr) { - if (! *lower_layer_edge_grid) { - // Create the distance field for a layer below. - const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5); - *lower_layer_edge_grid = make_unique(); - (*lower_layer_edge_grid)->create(m_layer->lower_layer->lslices, distance_field_resolution); - (*lower_layer_edge_grid)->calculate_sdf(); - #if 0 - { - static int iRun = 0; - BoundingBox bbox = (*lower_layer_edge_grid)->bbox(); - bbox.min(0) -= scale_(5.f); - bbox.min(1) -= scale_(5.f); - bbox.max(0) += scale_(5.f); - bbox.max(1) += scale_(5.f); - EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++)); - } - #endif - } - } + if (m_layer->lower_layer && lower_layer_edge_grid != nullptr && ! *lower_layer_edge_grid) + *lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer); // extrude all loops ccw bool was_clockwise = loop.make_counter_clockwise(); @@ -2640,6 +2645,10 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vectorlower_layer && ! lower_layer_edge_grid) + lower_layer_edge_grid = calculate_layer_edge_grid(*m_layer->lower_layer); + m_seam_placer.plan_perimeters(std::vector(region.perimeters.begin(), region.perimeters.end()), *m_layer, m_config.seam_position, this->last_pos(), EXTRUDER_CONFIG(nozzle_diameter), (m_layer == NULL ? nullptr : m_layer->object()), diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 7d6940bd0..349f17930 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -673,6 +673,7 @@ void GUI_App::post_init() // to popup a modal dialog on start without screwing combo boxes. // This is ugly but I honestly found no better way to do it. // Neither wxShowEvent nor wxWindowCreateEvent work reliably. + assert(this->preset_updater); // FIXME Following condition is probably not neccessary. if (this->preset_updater) { this->check_updates(false); CallAfter([this] {