From 1164449d4e44451bbb9bec51f723e1336e2d22d8 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 29 Mar 2022 14:29:58 +0200 Subject: [PATCH] compute overhang distance using SDF detect embedded (inner) perimeter points and prefer them for seam placement --- src/libslic3r/GCode/SeamPlacer.cpp | 163 +++++++++++++++++------------ src/libslic3r/GCode/SeamPlacer.hpp | 8 +- 2 files changed, 102 insertions(+), 69 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 8070087a6f..c9c25f5191 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -127,6 +127,7 @@ std::vector raycast_visibility(const AABBTreeIndirect::Tree< BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: raycast visibility for " << triangles.indices.size() << " triangles: start"; + //prepare uniform samples of a hemisphere float step_size = 1.0f / SeamPlacer::sqr_rays_per_triangle; std::vector precomputed_sample_directions( SeamPlacer::sqr_rays_per_triangle * SeamPlacer::sqr_rays_per_triangle); @@ -344,7 +345,8 @@ struct GlobalModelInfo { ; //Extract perimeter polygons of the given layer -Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition configured_seam_preference) { +Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition configured_seam_preference, + std::vector &corresponding_regions_out) { Polygons polygons; for (const LayerRegion *layer_region : layer->regions()) { for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) { @@ -352,21 +354,24 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { if (perimeter->role() == ExtrusionRole::erExternalPerimeter || (perimeter->role() == ExtrusionRole::erPerimeter - && configured_seam_preference == spRandom)) { + && configured_seam_preference == spRandom)) { //for random seam alignment, extract all perimeters Points p; perimeter->collect_points(p); polygons.emplace_back(p); + corresponding_regions_out.push_back(layer_region); } } if (polygons.empty()) { Points p; ex_entity->collect_points(p); polygons.emplace_back(p); + corresponding_regions_out.push_back(layer_region); } } else { Points p; ex_entity->collect_points(p); polygons.emplace_back(p); + corresponding_regions_out.push_back(layer_region); } } } @@ -374,6 +379,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi if (polygons.empty()) { // If there are no perimeter polygons for whatever reason (disabled perimeters .. ) insert dummy point // it is easier than checking everywhere if the layer is not emtpy, no seam will be placed to this layer anyway polygons.emplace_back(std::vector { Point { 0, 0 } }); + corresponding_regions_out.push_back(nullptr); } return polygons; @@ -383,8 +389,8 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi // Compute its type (Enfrocer,Blocker), angle, and position //each SeamCandidate also contains pointer to shared Perimeter structure representing the polygon // if Custom Seam modifiers are present, oversamples the polygon if necessary to better fit user intentions -void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std::vector &result_vec, - const GlobalModelInfo &global_model_info) { +void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const LayerRegion *region, + const GlobalModelInfo &global_model_info, std::vector &result_vec) { if (orig_polygon.size() == 0) { return; } @@ -411,6 +417,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std:: std::queue oversampled_points { }; size_t orig_angle_index = 0; perimeter->start_index = result_vec.size(); + perimeter->flow_width = region != nullptr ? region->flow(FlowRole::frExternalPerimeter).width() : 0.0f; bool some_point_enforced = false; while (!orig_polygon_points.empty() || !oversampled_points.empty()) { EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral; @@ -501,36 +508,6 @@ std::pair find_previous_and_next_perimeter_point(const std::vect return {size_t(prev),size_t(next)}; } -//NOTE: only rough esitmation of overhang distance -// value represents distance from edge, positive is overhang, negative is inside shape -float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_a, const SeamCandidate &under_b, - const SeamCandidate &under_c) { - auto p = Vec2d { point.position.x(), point.position.y() }; - auto a = Vec2d { under_a.position.x(), under_a.position.y() }; - auto b = Vec2d { under_b.position.x(), under_b.position.y() }; - auto c = Vec2d { under_c.position.x(), under_c.position.y() }; - - auto oriented_line_dist = [](const Vec2d a, const Vec2d b, const Vec2d p) { //signed distance from line - return -((b.x() - a.x()) * (a.y() - p.y()) - (a.x() - p.x()) * (b.y() - a.y())) / (a - b).norm(); - }; - - auto dist_ab = oriented_line_dist(a, b, p); - auto dist_bc = oriented_line_dist(b, c, p); - - // from angle and signed distances from the arms of the points on the previous layer, we - // can deduce if it is overhang and give estimation of the size. - // However, the size of the overhang is rough estimation, the sign is more reliable - if (under_b.local_ccw_angle > 0 && dist_ab > 0 && dist_bc > 0) { //convex shape, p is inside - return -((p - b).norm() + dist_ab + dist_bc) / 3.0; - } - - if (under_b.local_ccw_angle < 0 && (dist_ab < 0 || dist_bc < 0)) { //concave shape, p is inside - return -((p - b).norm() + dist_ab + dist_bc) / 3.0; - } - - return ((p - b).norm() + dist_ab + dist_bc) / 3.0; -} - // Computes all global model info - transforms object, performs raycasting, // stores enforces and blockers void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po) { @@ -664,7 +641,15 @@ struct SeamComparator { } //avoid overhangs - if (a.overhang > 0.1f && b.overhang < a.overhang) { + if (a.overhang > 0.0f && b.overhang < a.overhang) { + return false; + } + + // prefer hidden points (more than 1 mm inside) + if (a.embedded_distance < -1.0f && b.embedded_distance > -1.0f) { + return true; + } + if (b.embedded_distance < -1.0f && a.embedded_distance > -1.0f) { return false; } @@ -719,7 +704,15 @@ struct SeamComparator { } //avoid overhangs - if (a.overhang > 0.1f && b.overhang < a.overhang) { + if (a.overhang > 0.0f && b.overhang < a.overhang) { + return false; + } + + // prefer hidden points (more than 1 mm inside) + if (a.embedded_distance < -1.0f && b.embedded_distance > -1.0f) { + return true; + } + if (b.embedded_distance < -1.0f && a.embedded_distance > -1.0f) { return false; } @@ -783,6 +776,10 @@ void debug_export_points(const std::vector())), weight_fill); + + Vec3i overhang_color = value_rgbi(-0.5, 0.5, std::clamp(point.overhang, -0.5f, 0.5f)); + std::string overhang_fill = "rgb(" + std::to_string(overhang_color.x()) + "," + + std::to_string(overhang_color.y()) + + "," + + std::to_string(overhang_color.z()) + ")"; + overhangs_svg.draw(scaled(Vec2f(point.position.head<2>())), overhang_fill); } } } @@ -899,6 +903,20 @@ void pick_random_seam_point(std::vector &perimeter_points, size_t } +EdgeGrid::Grid compute_layer_merged_edge_grid(const Layer *layer) { + static const float eps = float(scale_(layer->object()->config().slice_closing_radius.value)); + // merge with offset + ExPolygons merged = layer->merged(eps); + // ofsset back + ExPolygons layer_outline = offset_ex(merged, -eps); + + const coord_t distance_field_resolution = coord_t(scale_(1.) + 0.5); + EdgeGrid::Grid result { }; + result.create(layer_outline, distance_field_resolution); + result.calculate_sdf(); + return result; +} + } // namespace SeamPlacerImpl // Parallel process and extract each perimeter polygon of the given print object. @@ -918,15 +936,16 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po, m_perimeter_points_per_object[po][layer_idx]; const Layer *layer = po->get_layer(layer_idx); auto unscaled_z = layer->slice_z; - Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference); - for (const auto &poly : polygons) { - process_perimeter_polygon(poly, unscaled_z, layer_candidates, - global_model_info); + std::vector regions; + //NOTE corresponding region ptr may be null, if the layer has zero perimeters + Polygons polygons = extract_perimeter_polygons(layer, configured_seam_preference, regions); + for (size_t poly_index = 0; poly_index < polygons.size(); ++poly_index) { + process_perimeter_polygon(polygons[poly_index], unscaled_z, + regions[poly_index], global_model_info, layer_candidates); } auto functor = SeamCandidateCoordinateFunctor { &layer_candidates }; - m_perimeter_points_trees_per_object[po][layer_idx] = std::make_unique( - functor, layer_candidates.size()); + m_perimeter_points_trees_per_object[po][layer_idx] = + std::make_unique(functor, layer_candidates.size()); } } ); @@ -947,36 +966,45 @@ void SeamPlacer::calculate_candidates_visibility(const PrintObject *po, }); } -void SeamPlacer::calculate_overhangs(const PrintObject *po) { +void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) { using namespace SeamPlacerImpl; tbb::parallel_for(tbb::blocked_range(0, m_perimeter_points_per_object[po].size()), [&](tbb::blocked_range r) { + std::unique_ptr prev_layer_grid; + if (r.begin() > 0) { // previous layer exists + prev_layer_grid = std::make_unique( + compute_layer_merged_edge_grid(po->layers()[r.begin() - 1])); + } + for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { + bool layer_has_multiple_loops = + m_perimeter_points_per_object[po][layer_idx][0].perimeter->end_index + < m_perimeter_points_per_object[po][layer_idx].size() - 1; + std::unique_ptr current_layer_grid = std::make_unique( + compute_layer_merged_edge_grid(po->layers()[layer_idx])); + for (SeamCandidate &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) { - const auto calculate_layer_overhang = [&](size_t other_layer_idx) { - size_t closest_supporter = find_closest_point( - *m_perimeter_points_trees_per_object[po][other_layer_idx], - perimeter_point.position); - const SeamCandidate &supporter_point = - m_perimeter_points_per_object[po][other_layer_idx][closest_supporter]; + Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() }); + if (prev_layer_grid.get() != nullptr) { + coordf_t overhang_dist; + prev_layer_grid->signed_distance(point, scaled(perimeter_point.perimeter->flow_width), overhang_dist); + perimeter_point.overhang = + unscale(overhang_dist) - perimeter_point.perimeter->flow_width; + } - auto prev_next = find_previous_and_next_perimeter_point(m_perimeter_points_per_object[po][other_layer_idx], closest_supporter); - const SeamCandidate &prev_point = - m_perimeter_points_per_object[po][other_layer_idx][prev_next.first]; - const SeamCandidate &next_point = - m_perimeter_points_per_object[po][other_layer_idx][prev_next.second]; - - return calculate_overhang(perimeter_point, prev_point, - supporter_point, next_point); - }; - - if (layer_idx > 0) { //calculate overhang - perimeter_point.overhang = calculate_layer_overhang(layer_idx-1); + if (layer_has_multiple_loops) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) + coordf_t layer_embedded_distance; + current_layer_grid->signed_distance(point, scaled(1.0f), + layer_embedded_distance); + perimeter_point.embedded_distance = unscale(layer_embedded_distance); } } + + prev_layer_grid.swap(current_layer_grid); } - }); + } + ); } // Estimates, if there is good seam point in the layer_idx which is close to last_point_pos @@ -1243,10 +1271,10 @@ void SeamPlacer::init(const Print &print) { } BOOST_LOG_TRIVIAL(debug) - << "SeamPlacer: calculate_overhangs : start"; - calculate_overhangs(po); + << "SeamPlacer: calculate_overhangs and layer embdedding : start"; + calculate_overhangs_and_layer_embedding(po); BOOST_LOG_TRIVIAL(debug) - << "SeamPlacer: calculate_overhangs : end"; + << "SeamPlacer: calculate_overhangs and layer embdedding: end"; BOOST_LOG_TRIVIAL(debug) << "SeamPlacer: pick_seam_point : start"; @@ -1285,7 +1313,8 @@ void SeamPlacer::init(const Print &print) { } } -void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const { +void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, + const Point &last_pos) const { using namespace SeamPlacerImpl; const PrintObject *po = layer->object(); //NOTE this is necessary, since layer->id() is quite unreliable diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index d6b64c115c..130547ff82 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -40,6 +40,7 @@ struct Perimeter { size_t start_index; size_t end_index; //inclusive! size_t seam_index; + float flow_width; // During alignment, a final position may be stored here. In that case, finalized is set to true. // Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position @@ -55,7 +56,7 @@ struct SeamCandidate { SeamCandidate(const Vec3f &pos, std::shared_ptr perimeter, float local_ccw_angle, EnforcedBlockedSeamPoint type) : - position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), local_ccw_angle( + position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle( local_ccw_angle), type(type), central_enforcer(false) { } const Vec3f position; @@ -63,6 +64,9 @@ struct SeamCandidate { const std::shared_ptr perimeter; float visibility; float overhang; + // distance inside the merged layer regions, for detecting perimter points which are hidden indside the print (e.g. multimaterial join) + // Negative sign means inside the print, comes from EdgeGrid structure + float embedded_distance; float local_ccw_angle; EnforcedBlockedSeamPoint type; bool central_enforcer; //marks this candidate as central point of enforced segment on the perimeter - important for alignment @@ -131,7 +135,7 @@ private: const SeamPosition configured_seam_preference); void calculate_candidates_visibility(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info); - void calculate_overhangs(const PrintObject *po); + void calculate_overhangs_and_layer_embedding(const PrintObject *po); void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator); bool find_next_seam_in_layer(const PrintObject *po, std::pair &last_point_indexes,