From cb3678e1e250d9fe264e18cff119c22684370716 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 1 Aug 2022 12:07:36 +0200 Subject: [PATCH 01/10] fix bug in seam string weight - use seam string length as quality score --- src/libslic3r/GCode/SeamPlacer.cpp | 26 ++++++-------------------- src/libslic3r/GCode/SeamPlacer.hpp | 13 ++++++------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 2a716a300..056b7a17e 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -501,7 +501,6 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const } } - result.perimeters.push_back( { }); Perimeter &perimeter = result.perimeters.back(); @@ -1042,8 +1041,8 @@ public: float distance_from_perimeter(const Point &point) const { Vec2d p = unscale(point); - size_t hit_idx_out; - Vec2d hit_point_out; + size_t hit_idx_out{}; + Vec2d hit_point_out = Vec2d::Zero(); auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); if (distance < 0) { return std::numeric_limits::max(); @@ -1223,9 +1222,7 @@ std::optional> SeamPlacer::find_next_seam_in_layer( } std::vector> SeamPlacer::find_seam_string(const PrintObject *po, - std::pair start_seam, const SeamPlacerImpl::SeamComparator &comparator, - float& string_weight) const { - string_weight = 0.0f; + std::pair start_seam, const SeamPlacerImpl::SeamComparator &comparator) const { const std::vector &layers = m_seam_per_object.find(po)->second.layers; int layer_idx = start_seam.first; @@ -1259,11 +1256,6 @@ std::vector> SeamPlacer::find_seam_string(const PrintO if (maybe_next_seam.has_value()) { // For old macOS (pre 10.14), std::optional does not have .value() method, so the code is using operator*() instead. - std::pair next_seam_coords = maybe_next_seam.operator*(); - const auto &next_seam = layers[next_seam_coords.first].points[next_seam_coords.second]; - bool is_moved = next_seam.perimeter.seam_index != next_seam_coords.second; - string_weight += comparator.weight(next_seam) - - is_moved ? comparator.weight(layers[next_seam_coords.first].points[next_seam.perimeter.seam_index]) : 0.0f; seam_string.push_back(maybe_next_seam.operator*()); prev_point_index = seam_string.back(); //String added, prev_point_index updated @@ -1279,7 +1271,6 @@ std::vector> SeamPlacer::find_seam_string(const PrintO } next_layer += step; } - return seam_string; } @@ -1349,18 +1340,13 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: // This perimeter is already aligned, skip seam continue; } else { - float seam_string_weight; - seam_string = this->find_seam_string(po, { layer_idx, seam_index }, comparator, seam_string_weight); + seam_string = this->find_seam_string(po, { layer_idx, seam_index }, comparator); size_t step_size = 1 + seam_string.size() / 20; for (size_t alternative_start = 0; alternative_start < seam_string.size(); alternative_start+=step_size) { - float alternative_seam_string_weight = 0; size_t start_layer_idx = seam_string[alternative_start].first; size_t seam_idx = layers[start_layer_idx].points[seam_string[alternative_start].second].perimeter.seam_index; - alternative_seam_string = this->find_seam_string(po, std::pair(start_layer_idx, seam_idx), comparator, - alternative_seam_string_weight); - if (alternative_seam_string.size() >= SeamPlacer::seam_align_minimum_string_seams && - alternative_seam_string_weight > seam_string_weight) { - seam_string_weight = alternative_seam_string_weight; + alternative_seam_string = this->find_seam_string(po, std::pair(start_layer_idx, seam_idx), comparator); + if (alternative_seam_string.size() > seam_string.size()) { seam_string = std::move(alternative_seam_string); } } diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 1f1643d0d..327129e9d 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -52,16 +52,16 @@ enum class EnforcedBlockedSeamPoint { // struct representing single perimeter loop struct Perimeter { - size_t start_index; - size_t end_index; //inclusive! - size_t seam_index; - float flow_width; + 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 // Random position also uses this flexibility to set final seam point position bool finalized = false; - Vec3f final_seam_position; + Vec3f final_seam_position = Vec3f::Zero(); }; //Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created, @@ -167,8 +167,7 @@ private: void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator); std::vector> find_seam_string(const PrintObject *po, std::pair start_seam, - const SeamPlacerImpl::SeamComparator &comparator, - float& string_weight) const; + const SeamPlacerImpl::SeamComparator &comparator) const; std::optional> find_next_seam_in_layer( const std::vector &layers, const Vec3f& projected_position, From af95b6ecbd509c7e725111d30a988fd70fbd999f Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Mon, 1 Aug 2022 17:46:20 +0200 Subject: [PATCH 02/10] fix issue with enforced patches refactor end_index to mark index after the last point hackfix issue with overhang seem placement --- src/libslic3r/GCode/SeamPlacer.cpp | 149 +++++++++++++++++------------ src/libslic3r/GCode/SeamPlacer.hpp | 6 +- 2 files changed, 91 insertions(+), 64 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 056b7a17e..94086c56d 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -557,50 +557,72 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const result.points.emplace_back(position, perimeter, local_ccw_angle, type); } - perimeter.end_index = result.points.size() - 1; + perimeter.end_index = result.points.size(); - // We will find first patch of enforced points (patch: continuous section of enforced points) and select the middle - // point, which will have priority during alignment - // If there are multiple enforced patches in the perimeter, others are ignored if (some_point_enforced) { - size_t perimeter_size = perimeter.end_index - perimeter.start_index + 1; + // We will patches of enforced points (patch: continuous section of enforced points), choose + // the longest patch, and select the middle point or sharp point (depending on the angle) + // this point will have high priority on this perimeter + size_t perimeter_size = perimeter.end_index - perimeter.start_index; const auto next_index = [&](size_t idx) { return perimeter.start_index + Slic3r::next_idx_modulo(idx - perimeter.start_index, perimeter_size); }; - size_t first_enforced_idx = perimeter.start_index; - for (size_t _ = 0; _ < perimeter_size; ++_) { - if (result.points[first_enforced_idx].type != EnforcedBlockedSeamPoint::Enforced && - result.points[next_index(first_enforced_idx)].type == EnforcedBlockedSeamPoint::Enforced) { - break; + std::vector patches_starts_ends; + for (size_t i = perimeter.start_index; i < perimeter.end_index; ++i) { + if (result.points[i].type != EnforcedBlockedSeamPoint::Enforced && + result.points[next_index(i)].type == EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(next_index(i)); + } + if (result.points[i].type == EnforcedBlockedSeamPoint::Enforced && + result.points[next_index(i)].type != EnforcedBlockedSeamPoint::Enforced) { + patches_starts_ends.push_back(next_index(i)); } - first_enforced_idx = next_index(first_enforced_idx); } - first_enforced_idx = next_index(first_enforced_idx); - - // Gather also points with large angles (these are points from the original mesh, since oversampled points have zero angle) - // If there are any, the middle point will be picked from those (makes drawing over sharp corners easier) - std::vector orig_large_angle_points_indices { }; - std::vector viable_points_indices { }; - size_t last_enforced_idx = first_enforced_idx; - for (size_t _ = 0; _ < perimeter_size; ++_) { - if (result.points[last_enforced_idx].type != EnforcedBlockedSeamPoint::Enforced) { - break; + //if patches_starts_ends are empty, it means that the whole perimeter is enforced.. don't do anything in that case + if (!patches_starts_ends.empty()) { + //if the first point in the patches is not enforced, it marks a patch end. in that case, put it to the end and start on next + // to simplify the processing + assert(patches_starts_ends.size() % 2 == 0); + bool start_on_second = false; + if (result.points[patches_starts_ends[0]].type != EnforcedBlockedSeamPoint::Enforced) { + start_on_second = true; + patches_starts_ends.push_back(patches_starts_ends[0]); } - viable_points_indices.push_back(last_enforced_idx); - if (compute_angle_penalty(result.points[last_enforced_idx].local_ccw_angle) - < SeamPlacer::sharp_angle_penalty_snapping_threshold) { - orig_large_angle_points_indices.push_back(last_enforced_idx); + //now pick the longest patch + std::pair longest_patch { 0, 0 }; + auto patch_len = [perimeter_size](const std::pair &start_end) { + if (start_end.second < start_end.first) { + return start_end.first + (perimeter_size - start_end.second); + } else { + return start_end.second - start_end.first; + } + }; + for (size_t patch_idx = start_on_second ? 1 : 0; patch_idx < patches_starts_ends.size(); patch_idx += 2) { + std::pair current_patch { patches_starts_ends[patch_idx], patches_starts_ends[patch_idx + + 1] }; + if (patch_len(longest_patch) < patch_len(current_patch)) { + longest_patch = current_patch; + } + } + std::vector viable_points_indices; + std::vector large_angle_points_indices; + for (size_t point_idx = longest_patch.first; point_idx != longest_patch.second; + point_idx = next_index(point_idx)) { + viable_points_indices.push_back(point_idx); + if (compute_angle_penalty(result.points[point_idx].local_ccw_angle) + < SeamPlacer::sharp_angle_penalty_snapping_threshold) { + large_angle_points_indices.push_back(point_idx); + } + } + assert(viable_points_indices.size() > 0); + if (large_angle_points_indices.empty()) { + size_t central_idx = viable_points_indices[viable_points_indices.size() / 2]; + result.points[central_idx].central_enforcer = true; + } else { + size_t central_idx = large_angle_points_indices.size() / 2; + result.points[large_angle_points_indices[central_idx]].central_enforcer = true; } - last_enforced_idx = next_index(last_enforced_idx); - } - assert(viable_points_indices.size() > 0); - if (orig_large_angle_points_indices.empty()) { - size_t central_idx = viable_points_indices[viable_points_indices.size() / 2]; - result.points[central_idx].central_enforcer = true; - } else { - size_t central_idx = orig_large_angle_points_indices.size() / 2; - result.points[orig_large_angle_points_indices[central_idx]].central_enforcer = true; } } @@ -620,7 +642,7 @@ std::pair find_previous_and_next_perimeter_point(const std::vect prev = current.perimeter.end_index; } - if (point_index == current.perimeter.end_index) { + if (point_index == current.perimeter.end_index - 1) { // if point_index is equal to end, than next neighbour is at the start next = current.perimeter.start_index; } @@ -748,7 +770,8 @@ struct SeamComparator { float angle_importance; explicit SeamComparator(SeamPosition setup) : setup(setup) { - angle_importance = setup == spNearest ? SeamPlacer::angle_importance_nearest : SeamPlacer::angle_importance_aligned; + angle_importance = + setup == spNearest ? SeamPlacer::angle_importance_nearest : SeamPlacer::angle_importance_aligned; } // Standard comparator, must respect the requirements of comparators (e.g. give same result on same inputs) for sorting usage @@ -765,8 +788,7 @@ struct SeamComparator { } //avoid overhangs - if (a.overhang > SeamPlacer::overhang_distance_tolerance_factor * a.perimeter.flow_width || - b.overhang > SeamPlacer::overhang_distance_tolerance_factor * b.perimeter.flow_width) { + if (a.overhang > 0.0f || b.overhang > 0.0f) { return a.overhang < b.overhang; } @@ -823,8 +845,8 @@ struct SeamComparator { } //avoid overhangs - if (a.overhang > SeamPlacer::overhang_distance_tolerance_factor * a.perimeter.flow_width || - b.overhang > SeamPlacer::overhang_distance_tolerance_factor * b.perimeter.flow_width) { + if ((a.overhang > 0.0f || b.overhang > 0.0f) + && abs(a.overhang - b.overhang) > (0.2f * a.perimeter.flow_width)) { return a.overhang < b.overhang; } @@ -857,9 +879,9 @@ struct SeamComparator { } float weight(const SeamCandidate &a) const { - if (setup == SeamPosition::spAligned && a.central_enforcer) { - return 2.0f; - } + if (setup == SeamPosition::spAligned && a.central_enforcer) { + return 2.0f; + } return a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) / (1.0f + angle_importance); } }; @@ -930,7 +952,7 @@ void pick_seam_point(std::vector &perimeter_points, size_t start_ size_t end_index = perimeter_points[start_index].perimeter.end_index; size_t seam_index = start_index; - for (size_t index = start_index; index <= end_index; ++index) { + for (size_t index = start_index; index < end_index; ++index) { if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index])) { seam_index = index; } @@ -944,7 +966,7 @@ size_t pick_nearest_seam_point_index(const std::vector &perimeter SeamComparator comparator { spNearest }; size_t seam_index = start_index; - for (size_t index = start_index; index <= end_index; ++index) { + for (size_t index = start_index; index < end_index; ++index) { if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index], preffered_location)) { seam_index = index; } @@ -971,10 +993,10 @@ void pick_random_seam_point(const std::vector &perimeter_points, }; std::vector viables; - for (size_t index = start_index; index <= end_index; ++index) { + for (size_t index = start_index; index < end_index; ++index) { if (comparator.are_similar(perimeter_points[index], perimeter_points[viable_example_index])) { // index ok, push info into viables - Vec3f edge_to_next { perimeter_points[index == end_index ? start_index : index + 1].position + Vec3f edge_to_next { perimeter_points[index == end_index - 1 ? start_index : index + 1].position - perimeter_points[index].position }; float dist_to_next = edge_to_next.norm(); viables.push_back( { index, dist_to_next, edge_to_next }); @@ -987,7 +1009,7 @@ void pick_random_seam_point(const std::vector &perimeter_points, viable_example_index = index; viables.clear(); - Vec3f edge_to_next = (perimeter_points[index == end_index ? start_index : index + 1].position + Vec3f edge_to_next = (perimeter_points[index == end_index - 1 ? start_index : index + 1].position - perimeter_points[index].position); float dist_to_next = edge_to_next.norm(); viables.push_back( { index, dist_to_next, edge_to_next }); @@ -1041,7 +1063,7 @@ public: float distance_from_perimeter(const Point &point) const { Vec2d p = unscale(point); - size_t hit_idx_out{}; + size_t hit_idx_out { }; Vec2d hit_point_out = Vec2d::Zero(); auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out); if (distance < 0) { @@ -1075,7 +1097,7 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po, tbb::parallel_for(tbb::blocked_range(0, po->layers().size()), [po, configured_seam_preference, arachne_generated, &global_model_info, &seam_data] - (tbb::blocked_range r) { + (tbb::blocked_range r) { for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx]; const Layer *layer = po->get_layer(layer_idx); @@ -1136,7 +1158,8 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) for (SeamCandidate &perimeter_point : layers[layer_idx].points) { Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() }); if (prev_layer_distancer.get() != nullptr) { - perimeter_point.overhang = prev_layer_distancer->distance_from_perimeter(point); + perimeter_point.overhang = prev_layer_distancer->distance_from_perimeter(point) + - tan(SeamPlacer::overhang_angle_threshold * PI / 180.0f) * po->layers()[layer_idx]->height; } if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) @@ -1263,8 +1286,8 @@ std::vector> SeamPlacer::find_seam_string(const PrintO if (step == 1) { reverse_lookup_direction(); if (next_layer < 0) { - break; - } + break; + } } else { break; } @@ -1309,7 +1332,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: size_t current_point_index = 0; while (current_point_index < layer_perimeter_points.size()) { seams.emplace_back(layer_idx, layer_perimeter_points[current_point_index].perimeter.seam_index); - current_point_index = layer_perimeter_points[current_point_index].perimeter.end_index + 1; + current_point_index = layer_perimeter_points[current_point_index].perimeter.end_index; } } @@ -1342,10 +1365,12 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: } else { seam_string = this->find_seam_string(po, { layer_idx, seam_index }, comparator); size_t step_size = 1 + seam_string.size() / 20; - for (size_t alternative_start = 0; alternative_start < seam_string.size(); alternative_start+=step_size) { + for (size_t alternative_start = 0; alternative_start < seam_string.size(); alternative_start += step_size) { size_t start_layer_idx = seam_string[alternative_start].first; - size_t seam_idx = layers[start_layer_idx].points[seam_string[alternative_start].second].perimeter.seam_index; - alternative_seam_string = this->find_seam_string(po, std::pair(start_layer_idx, seam_idx), comparator); + size_t seam_idx = + layers[start_layer_idx].points[seam_string[alternative_start].second].perimeter.seam_index; + alternative_seam_string = this->find_seam_string(po, + std::pair(start_layer_idx, seam_idx), comparator); if (alternative_seam_string.size() > seam_string.size()) { seam_string = std::move(alternative_seam_string); } @@ -1487,7 +1512,7 @@ void SeamPlacer::init(const Print &print, std::function throw_if_can for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { std::vector &layer_perimeter_points = layers[layer_idx].points; for (size_t current = 0; current < layer_perimeter_points.size(); - current = layer_perimeter_points[current].perimeter.end_index + 1) + current = layer_perimeter_points[current].perimeter.end_index) if (configured_seam_preference == spRandom) pick_random_seam_point(layer_perimeter_points, current); else @@ -1561,12 +1586,12 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern // the internal seam into the concave corner, and not on the perpendicular projection on the closest edge (which is what the split_at function does) size_t index_of_prev = seam_index == perimeter_point.perimeter.start_index ? - perimeter_point.perimeter.end_index : + perimeter_point.perimeter.end_index - 1 : seam_index - 1; size_t index_of_next = - seam_index == perimeter_point.perimeter.end_index ? - perimeter_point.perimeter.start_index : - seam_index + 1; + seam_index == perimeter_point.perimeter.end_index - 1 ? + perimeter_point.perimeter.start_index : + seam_index + 1; Vec2f dir_to_middle = ((perimeter_point.position - layer_perimeters.points[index_of_prev].position).head<2>().normalized() diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 327129e9d..30f077e1a 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -131,8 +131,10 @@ public: // value for angles with penalty lower than this threshold - such angles will be snapped to their original position instead of spline interpolated position static constexpr float sharp_angle_penalty_snapping_threshold = 0.6f; - // max tolerable distance from the previous layer is overhang_distance_tolerance_factor * flow_width - static constexpr float overhang_distance_tolerance_factor = 0.5f; + // max allowed overhang angle for seam placement, in degrees, measured from vertical direction + //TODO There is some problem in layer distance computation, so the current angle is much lower to counter that issue + // However, it should be fixed correctly + static constexpr float overhang_angle_threshold = 15.0f; // determines angle importance compared to visibility ( neutral value is 1.0f. ) static constexpr float angle_importance_aligned = 0.6f; From a41435d0449e3cd6ccf170189a2a8130fd3ad1e1 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 2 Aug 2022 10:05:48 +0200 Subject: [PATCH 03/10] fixed overhang estimation correclty --- src/libslic3r/GCode/SeamPlacer.cpp | 7 +++++-- src/libslic3r/GCode/SeamPlacer.hpp | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 94086c56d..5b7e525af 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1159,11 +1159,14 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() }); if (prev_layer_distancer.get() != nullptr) { perimeter_point.overhang = prev_layer_distancer->distance_from_perimeter(point) - - tan(SeamPlacer::overhang_angle_threshold * PI / 180.0f) * po->layers()[layer_idx]->height; + + 0.5f * perimeter_point.perimeter.flow_width + - tan(SeamPlacer::overhang_angle_threshold * PI / 180.0f) + * po->layers()[layer_idx]->height; } if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) - perimeter_point.embedded_distance = current_layer_distancer->distance_from_perimeter(point); + perimeter_point.embedded_distance = current_layer_distancer->distance_from_perimeter(point) + + 0.5f * perimeter_point.perimeter.flow_width; } } diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 30f077e1a..0a6f013d0 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -134,7 +134,7 @@ public: // max allowed overhang angle for seam placement, in degrees, measured from vertical direction //TODO There is some problem in layer distance computation, so the current angle is much lower to counter that issue // However, it should be fixed correctly - static constexpr float overhang_angle_threshold = 15.0f; + static constexpr float overhang_angle_threshold = 50.0f; // determines angle importance compared to visibility ( neutral value is 1.0f. ) static constexpr float angle_importance_aligned = 0.6f; From 44a388d560cd1641be91a9062969c75375475a35 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Tue, 2 Aug 2022 17:10:21 +0200 Subject: [PATCH 04/10] prefer enforcers over blockers, lower the threshold distance, dynamic modification of segments count in alignment based on smoothnes --- src/libslic3r/GCode/SeamPlacer.cpp | 70 +++++++++++------------------- src/libslic3r/GCode/SeamPlacer.hpp | 4 +- src/libslic3r/Geometry/Curves.hpp | 13 ++++++ 3 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 5b7e525af..6fbd89306 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -467,7 +467,7 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi //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, const LayerRegion *region, - bool arachne_generated, const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result) { + const GlobalModelInfo &global_model_info, PrintObjectSeamData::LayerSeams &result) { if (orig_polygon.size() == 0) { return; } @@ -482,25 +482,6 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const std::vector polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths, SeamPlacer::polygon_local_angles_arm_distance); - // resample smooth surfaces from arachne, so that alignment finds short path down, and does not create unnecesary curves - if (arachne_generated && std::all_of(polygon_angles.begin(), polygon_angles.end(), [](float angle) { - return compute_angle_penalty(angle) > SeamPlacer::sharp_angle_penalty_snapping_threshold; - })) { - float total_dist = std::accumulate(lengths.begin(), lengths.end(), 0.0f); - float avg_dist = total_dist / float(lengths.size()); - if (avg_dist < SeamPlacer::seam_align_tolerable_dist * 2.0f){ - coord_t sampling_dist = scaled(avg_dist*0.2f); - - polygon.points = polygon.equally_spaced_points(sampling_dist); - lengths.clear(); - for (size_t point_idx = 0; point_idx < polygon.size() - 1; ++point_idx) { - lengths.push_back((unscale(polygon[point_idx]) - unscale(polygon[point_idx + 1])).norm()); - } - lengths.push_back(std::max((unscale(polygon[0]) - unscale(polygon[polygon.size() - 1])).norm(), 0.1)); - polygon_angles = calculate_polygon_angles_at_vertices(polygon, lengths, avg_dist); - } - } - result.perimeters.push_back( { }); Perimeter &perimeter = result.perimeters.back(); @@ -531,13 +512,14 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const orig_point = true; } - if (global_model_info.is_enforced(position, perimeter.flow_width)) { + if (global_model_info.is_blocked(position, perimeter.flow_width * 0.5f)) { + type = EnforcedBlockedSeamPoint::Blocked; + } + + if (global_model_info.is_enforced(position, perimeter.flow_width * 0.5f)) { type = EnforcedBlockedSeamPoint::Enforced; } - if (global_model_info.is_blocked(position, perimeter.flow_width)) { - type = EnforcedBlockedSeamPoint::Blocked; - } some_point_enforced = some_point_enforced || type == EnforcedBlockedSeamPoint::Enforced; if (orig_point) { @@ -880,9 +862,9 @@ struct SeamComparator { float weight(const SeamCandidate &a) const { if (setup == SeamPosition::spAligned && a.central_enforcer) { - return 2.0f; + return 10.0f; } - return a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) / (1.0f + angle_importance); + return 1.0f / (0.1f + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) / (1.0f + angle_importance)); } }; @@ -907,8 +889,8 @@ void debug_export_points(const std::vector &lay min_vis = std::min(min_vis, point.visibility); max_vis = std::max(max_vis, point.visibility); - min_weight = std::min(min_weight, -comparator.compute_angle_penalty(point.local_ccw_angle)); - max_weight = std::max(max_weight, -comparator.compute_angle_penalty(point.local_ccw_angle)); + min_weight = std::min(min_weight, -compute_angle_penalty(point.local_ccw_angle)); + max_weight = std::max(max_weight, -compute_angle_penalty(point.local_ccw_angle)); } @@ -929,7 +911,7 @@ void debug_export_points(const std::vector &lay visibility_svg.draw(scaled(Vec2f(point.position.head<2>())), visibility_fill); Vec3i weight_color = value_to_rgbi(min_weight, max_weight, - -comparator.compute_angle_penalty(point.local_ccw_angle)); + -compute_angle_penalty(point.local_ccw_angle)); std::string weight_fill = "rgb(" + std::to_string(weight_color.x()) + "," + std::to_string(weight_color.y()) + "," + std::to_string(weight_color.z()) + ")"; @@ -1090,13 +1072,11 @@ public: void SeamPlacer::gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference) { using namespace SeamPlacerImpl; - bool arachne_generated = po->config().perimeter_generator == PerimeterGeneratorType::Arachne; - PrintObjectSeamData &seam_data = m_seam_per_object.emplace(po, PrintObjectSeamData { }).first->second; seam_data.layers.resize(po->layer_count()); tbb::parallel_for(tbb::blocked_range(0, po->layers().size()), - [po, configured_seam_preference, arachne_generated, &global_model_info, &seam_data] + [po, configured_seam_preference, &global_model_info, &seam_data] (tbb::blocked_range r) { for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) { PrintObjectSeamData::LayerSeams &layer_seams = seam_data.layers[layer_idx]; @@ -1107,7 +1087,7 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po, 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], arachne_generated, global_model_info, layer_seams); + regions[poly_index], global_model_info, layer_seams); } auto functor = SeamCandidateCoordinateFunctor { layer_seams.points }; seam_data.layers[layer_idx].points_tree = @@ -1393,36 +1373,38 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: //repeat the alignment for the current seam, since it could be skipped due to alternative path being aligned. global_index--; - // gather all positions of seams and their weights (weights are derived as negative penalty, they are made positive in next step) + // gather all positions of seams and their weights observations.resize(seam_string.size()); observation_points.resize(seam_string.size()); weights.resize(seam_string.size()); //gather points positions and weights - float total_length = 0.0f; + float sharp_length = 0.0f; Vec3f last_point_pos = layers[seam_string[0].first].points[seam_string[0].second].position; for (size_t index = 0; index < seam_string.size(); ++index) { - Vec3f pos = layers[seam_string[index].first].points[seam_string[index].second].position; - total_length += (last_point_pos - pos).norm(); - last_point_pos = pos; + const SeamCandidate &point = layers[seam_string[index].first].points[seam_string[index].second]; + Vec3f pos = point.position; observations[index] = pos.head<2>(); observation_points[index] = pos.z(); - weights[index] = comparator.weight(layers[seam_string[index].first].points[seam_string[index].second]); + weights[index] = comparator.weight(point); + float angle_penalty = compute_angle_penalty(point.local_ccw_angle); + float dist = (last_point_pos - pos).norm(); + sharp_length += dist * 1.0f / (0.1f + 0.7f*angle_penalty); + bool is_enforced = point.type == EnforcedBlockedSeamPoint::Enforced; + if (is_enforced) sharp_length+= dist; + last_point_pos = pos; } // Curve Fitting size_t number_of_segments = std::max(size_t(1), - size_t(total_length / SeamPlacer::seam_align_mm_per_segment)); + size_t(sharp_length / SeamPlacer::seam_align_sharp_mm_per_segment)); auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments); // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into // Perimeter structure of the point; also set flag aligned to true for (size_t index = 0; index < seam_string.size(); ++index) { const auto &pair = seam_string[index]; - const float t = - compute_angle_penalty(layers[pair.first].points[pair.second].local_ccw_angle) - < SeamPlacer::sharp_angle_penalty_snapping_threshold - ? 0.8f : 0.0f; + const float t = std::min(1.0f, weights[index] / 10.0f); Vec3f current_pos = layers[pair.first].points[pair.second].position; Vec2f fitted_pos = curve.get_fitted_value(current_pos.z()); diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index 0a6f013d0..cdbd1b582 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -150,8 +150,8 @@ public: static constexpr float seam_align_tolerable_dist = 1.0f; // minimum number of seams needed in cluster to make alignment happen static constexpr size_t seam_align_minimum_string_seams = 6; - // millimeters covered by spline; determines number of splines for the given string - static constexpr size_t seam_align_mm_per_segment = 4.0f; + // millimeters of sharp corners covered by spline; determines number of splines for the given string + static constexpr float seam_align_sharp_mm_per_segment = 4.0f; //The following data structures hold all perimeter points for all PrintObject. std::unordered_map m_seam_per_object; diff --git a/src/libslic3r/Geometry/Curves.hpp b/src/libslic3r/Geometry/Curves.hpp index 6ccdea366..f4a5a0067 100644 --- a/src/libslic3r/Geometry/Curves.hpp +++ b/src/libslic3r/Geometry/Curves.hpp @@ -175,6 +175,19 @@ PiecewiseFittedCurve fit_curve( return result; } + +template +PiecewiseFittedCurve> +fit_linear_spline( + const std::vector> &observations, + std::vector observation_points, + std::vector weights, + size_t segments_count, + size_t endpoints_level_of_freedom = 0) { + return fit_curve>(observations, observation_points, weights, segments_count, + endpoints_level_of_freedom); +} + template PiecewiseFittedCurve> fit_cubic_bspline( From 52b9325f7c1c183ff5711b384e5f08e20eeac960 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 3 Aug 2022 14:52:33 +0200 Subject: [PATCH 05/10] improving dynamic segmentation, simplifications in snapping angle logic --- src/libslic3r/GCode/SeamPlacer.cpp | 68 ++++++++++++++---------------- src/libslic3r/GCode/SeamPlacer.hpp | 19 ++++----- 2 files changed, 40 insertions(+), 47 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 6fbd89306..940805c47 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -36,7 +36,7 @@ namespace SeamPlacerImpl { // ************ FOR BACKPORT COMPATIBILITY ONLY *************** // Color mapping of a value into RGB false colors. inline Vec3f value_to_rgbf(float minimum, float maximum, float value) -{ + { float ratio = 2.0f * (value - minimum) / (maximum - minimum); float b = std::max(0.0f, (1.0f - ratio)); float r = std::max(0.0f, (ratio - 1.0f)); @@ -46,7 +46,7 @@ inline Vec3f value_to_rgbf(float minimum, float maximum, float value) // Color mapping of a value into RGB false colors. inline Vec3i value_to_rgbi(float minimum, float maximum, float value) -{ + { return (value_to_rgbf(minimum, maximum, value) * 255).cast(); } // *************************** @@ -337,7 +337,7 @@ struct GlobalModelInfo { return 1.0f; } - auto compute_dist_to_plane = [](const Vec3f& position, const Vec3f& plane_origin, const Vec3f& plane_normal) { + auto compute_dist_to_plane = [](const Vec3f &position, const Vec3f &plane_origin, const Vec3f &plane_normal) { Vec3f orig_to_point = position - plane_origin; return std::abs(orig_to_point.dot(plane_normal)); }; @@ -421,9 +421,9 @@ Polygons extract_perimeter_polygons(const Layer *layer, const SeamPosition confi if (ex_entity->is_collection()) { //collection of inner, outer, and overhang perimeters for (const ExtrusionEntity *perimeter : static_cast(ex_entity)->entities) { ExtrusionRole role = perimeter->role(); - if (perimeter->is_loop()){ - for (const ExtrusionPath& path : static_cast(perimeter)->paths){ - if (path.role() == ExtrusionRole::erExternalPerimeter){ + if (perimeter->is_loop()) { + for (const ExtrusionPath &path : static_cast(perimeter)->paths) { + if (path.role() == ExtrusionRole::erExternalPerimeter) { role = ExtrusionRole::erExternalPerimeter; } } @@ -512,11 +512,11 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const orig_point = true; } - if (global_model_info.is_blocked(position, perimeter.flow_width * 0.5f)) { + if (global_model_info.is_blocked(position, perimeter.flow_width)) { type = EnforcedBlockedSeamPoint::Blocked; } - if (global_model_info.is_enforced(position, perimeter.flow_width * 0.5f)) { + if (global_model_info.is_enforced(position, perimeter.flow_width)) { type = EnforcedBlockedSeamPoint::Enforced; } @@ -592,8 +592,8 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const for (size_t point_idx = longest_patch.first; point_idx != longest_patch.second; point_idx = next_index(point_idx)) { viable_points_indices.push_back(point_idx); - if (compute_angle_penalty(result.points[point_idx].local_ccw_angle) - < SeamPlacer::sharp_angle_penalty_snapping_threshold) { + if (std::abs(result.points[point_idx].local_ccw_angle) + > SeamPlacer::sharp_angle_snapping_threshold) { large_angle_points_indices.push_back(point_idx); } } @@ -794,10 +794,10 @@ struct SeamComparator { } // the penalites are kept close to range [0-1.x] however, it should not be relied upon - float penalty_a = a.visibility + + float penalty_a = a.overhang + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) + distance_penalty_a; - float penalty_b = b.visibility + + float penalty_b = b.overhang + b.visibility + angle_importance * compute_angle_penalty(b.local_ccw_angle) + distance_penalty_b; @@ -848,9 +848,9 @@ struct SeamComparator { return a.position.y() + SeamPlacer::seam_align_score_tolerance * 5.0f > b.position.y(); } - float penalty_a = a.visibility + float penalty_a = a.overhang + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle); - float penalty_b = b.visibility + + float penalty_b = b.overhang + b.visibility + angle_importance * compute_angle_penalty(b.local_ccw_angle); return penalty_a <= penalty_b || penalty_a - penalty_b < SeamPlacer::seam_align_score_tolerance; @@ -859,13 +859,6 @@ struct SeamComparator { bool are_similar(const SeamCandidate &a, const SeamCandidate &b) const { return is_first_not_much_worse(a, b) && is_first_not_much_worse(b, a); } - - float weight(const SeamCandidate &a) const { - if (setup == SeamPosition::spAligned && a.central_enforcer) { - return 10.0f; - } - return 1.0f / (0.1f + a.visibility + angle_importance * compute_angle_penalty(a.local_ccw_angle) / (1.0f + angle_importance)); - } }; #ifdef DEBUG_FILES @@ -1138,10 +1131,11 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) for (SeamCandidate &perimeter_point : layers[layer_idx].points) { Point point = Point::new_scale(Vec2f { perimeter_point.position.head<2>() }); if (prev_layer_distancer.get() != nullptr) { - perimeter_point.overhang = prev_layer_distancer->distance_from_perimeter(point) + perimeter_point.overhang = (prev_layer_distancer->distance_from_perimeter(point) + 0.5f * perimeter_point.perimeter.flow_width - - tan(SeamPlacer::overhang_angle_threshold * PI / 180.0f) - * po->layers()[layer_idx]->height; + - tan(SeamPlacer::overhang_angle_threshold) + * po->layers()[layer_idx]->height) + / (3.0f * perimeter_point.perimeter.flow_width); } if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) @@ -1165,7 +1159,7 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) // Used by align_seam_points(). std::optional> SeamPlacer::find_next_seam_in_layer( const std::vector &layers, - const Vec3f& projected_position, + const Vec3f &projected_position, const size_t layer_idx, const float max_distance, const SeamPlacerImpl::SeamComparator &comparator) const { using namespace SeamPlacerImpl; @@ -1251,7 +1245,8 @@ std::vector> SeamPlacer::find_seam_string(const PrintO break; } } - float max_distance = SeamPlacer::seam_align_tolerable_dist; + float max_distance = SeamPlacer::seam_align_tolerable_dist_factor * + layers[start_seam.first].points[start_seam.second].perimeter.flow_width; Vec3f prev_position = layers[prev_point_index.first].points[prev_point_index.second].position; Vec3f projected_position = prev_position; projected_position.z() = float(po->get_layer(next_layer)->slice_z); @@ -1378,26 +1373,27 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: observation_points.resize(seam_string.size()); weights.resize(seam_string.size()); + auto angle_weight = [](float angle){ + return 1.0f / (0.1f + compute_angle_penalty(angle)); + }; + float sharp_angle_weight = angle_weight(SeamPlacer::sharp_angle_snapping_threshold); + //gather points positions and weights - float sharp_length = 0.0f; - Vec3f last_point_pos = layers[seam_string[0].first].points[seam_string[0].second].position; + size_t segments_count = 0; for (size_t index = 0; index < seam_string.size(); ++index) { const SeamCandidate &point = layers[seam_string[index].first].points[seam_string[index].second]; Vec3f pos = point.position; observations[index] = pos.head<2>(); observation_points[index] = pos.z(); - weights[index] = comparator.weight(point); - float angle_penalty = compute_angle_penalty(point.local_ccw_angle); - float dist = (last_point_pos - pos).norm(); - sharp_length += dist * 1.0f / (0.1f + 0.7f*angle_penalty); + weights[index] = angle_weight(point.local_ccw_angle); bool is_enforced = point.type == EnforcedBlockedSeamPoint::Enforced; - if (is_enforced) sharp_length+= dist; - last_point_pos = pos; + if (is_enforced || weights[index] > sharp_angle_weight) { + segments_count++; + } } // Curve Fitting - size_t number_of_segments = std::max(size_t(1), - size_t(sharp_length / SeamPlacer::seam_align_sharp_mm_per_segment)); + size_t number_of_segments = std::max(size_t(1), size_t(segments_count / SeamPlacer::seam_align_seams_per_segment)); auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments); // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index cdbd1b582..cc1e50513 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -128,13 +128,10 @@ public: // arm length used during angles computation static constexpr float polygon_local_angles_arm_distance = 0.3f; - // value for angles with penalty lower than this threshold - such angles will be snapped to their original position instead of spline interpolated position - static constexpr float sharp_angle_penalty_snapping_threshold = 0.6f; - - // max allowed overhang angle for seam placement, in degrees, measured from vertical direction - //TODO There is some problem in layer distance computation, so the current angle is much lower to counter that issue - // However, it should be fixed correctly - static constexpr float overhang_angle_threshold = 50.0f; + // snapping angle - angles larger than this value will be snapped to during seam painting + static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f; + // overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction + static constexpr float overhang_angle_threshold = 50.0f * float(PI) / 180.0f; // determines angle importance compared to visibility ( neutral value is 1.0f. ) static constexpr float angle_importance_aligned = 0.6f; @@ -146,12 +143,12 @@ public: // When searching for seam clusters for alignment: // following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer static constexpr float seam_align_score_tolerance = 0.3f; - // seam_align_tolerable_dist - if next layer closest point is too far away, break aligned string - static constexpr float seam_align_tolerable_dist = 1.0f; + // seam_align_tolerable_dist_factor - how far to search for seam from current position, final dist is seam_align_tolerable_dist_factor * flow_width + static constexpr float seam_align_tolerable_dist_factor = 4.0f; // minimum number of seams needed in cluster to make alignment happen static constexpr size_t seam_align_minimum_string_seams = 6; - // millimeters of sharp corners covered by spline; determines number of splines for the given string - static constexpr float seam_align_sharp_mm_per_segment = 4.0f; + // number of seams per bspline segment + static constexpr float seam_align_seams_per_segment = 8.0f; //The following data structures hold all perimeter points for all PrintObject. std::unordered_map m_seam_per_object; From 3f361871016c8f3106326cd16d91767f1d876a61 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 3 Aug 2022 15:34:41 +0200 Subject: [PATCH 06/10] increase segments count for smooth long strings --- src/libslic3r/GCode/SeamPlacer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 940805c47..0960b96ec 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1393,7 +1393,9 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: } // Curve Fitting - size_t number_of_segments = std::max(size_t(1), size_t(segments_count / SeamPlacer::seam_align_seams_per_segment)); + size_t number_of_segments = std::max( + std::max(size_t(1), seam_string.size() / 80), + size_t(segments_count / SeamPlacer::seam_align_seams_per_segment)); auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments); // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into From 06084b0bc53efe2167cc53180953895c90f2cd1e Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Wed, 3 Aug 2022 18:05:10 +0200 Subject: [PATCH 07/10] lower segment count on smooth parts --- src/libslic3r/GCode/SeamPlacer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 0960b96ec..72b9412d3 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1394,7 +1394,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: // Curve Fitting size_t number_of_segments = std::max( - std::max(size_t(1), seam_string.size() / 80), + std::max(size_t(1), seam_string.size() / 200), size_t(segments_count / SeamPlacer::seam_align_seams_per_segment)); auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments); From 0479387b2099ee632fde5e93e5b7e93bcca9b6b7 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 4 Aug 2022 11:52:51 +0200 Subject: [PATCH 08/10] reduce wavy seams for all layer heights, improve painting --- src/libslic3r/GCode/SeamPlacer.cpp | 18 +++++++++++++----- src/libslic3r/GCode/SeamPlacer.hpp | 4 ++-- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 72b9412d3..f824a50e1 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1379,23 +1379,31 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: float sharp_angle_weight = angle_weight(SeamPlacer::sharp_angle_snapping_threshold); //gather points positions and weights - size_t segments_count = 0; + float total_length = 0.0f; + float sharp_length = 0.0f; + Vec3f prev_pos = layers[seam_string[0].first].points[seam_string[0].second].position; for (size_t index = 0; index < seam_string.size(); ++index) { const SeamCandidate &point = layers[seam_string[index].first].points[seam_string[index].second]; Vec3f pos = point.position; + float dist = (pos - prev_pos).norm(); + total_length += dist; observations[index] = pos.head<2>(); observation_points[index] = pos.z(); weights[index] = angle_weight(point.local_ccw_angle); bool is_enforced = point.type == EnforcedBlockedSeamPoint::Enforced; - if (is_enforced || weights[index] > sharp_angle_weight) { - segments_count++; + if (is_enforced) { + weights[index] = std::max(weights[index], 6.0f); } + if (is_enforced || weights[index] > sharp_angle_weight) { + sharp_length+= dist; + } + prev_pos = pos; } // Curve Fitting size_t number_of_segments = std::max( - std::max(size_t(1), seam_string.size() / 200), - size_t(segments_count / SeamPlacer::seam_align_seams_per_segment)); + std::max(size_t(1), size_t(total_length / 30.0f)), + size_t(sharp_length / SeamPlacer::seam_align_mm_per_segment)); auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments); // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index cc1e50513..c905fe8f6 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -147,8 +147,8 @@ public: static constexpr float seam_align_tolerable_dist_factor = 4.0f; // minimum number of seams needed in cluster to make alignment happen static constexpr size_t seam_align_minimum_string_seams = 6; - // number of seams per bspline segment - static constexpr float seam_align_seams_per_segment = 8.0f; + // millimeters covered by spline; determines number of splines for the given string + static constexpr size_t seam_align_mm_per_segment = 4.0f; //The following data structures hold all perimeter points for all PrintObject. std::unordered_map m_seam_per_object; From 8812036c25931599a4bed66505aa1ec392a6d73f Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 5 Aug 2022 11:11:58 +0200 Subject: [PATCH 09/10] slowly decreasing angle seam placement feature disabled --- src/libslic3r/GCode/SeamPlacer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index f824a50e1..96b6dcd58 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -1136,6 +1136,8 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) - tan(SeamPlacer::overhang_angle_threshold) * po->layers()[layer_idx]->height) / (3.0f * perimeter_point.perimeter.flow_width); + //NOTE disables the feature to place seams on slowly decreasing areas. Remove the following line to enable. + perimeter_point.overhang = perimeter_point.overhang > 0.0f ? 0.0f : perimeter_point.overhang; } if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) From d39d35cc8367f2006b8db7553ce8ca99565b4f8f Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Fri, 5 Aug 2022 16:33:59 +0200 Subject: [PATCH 10/10] better curling fix, removed resamping, fixed the feature disable from previous commit --- src/libslic3r/GCode/SeamPlacer.cpp | 62 +++++++++++++++++------------- src/libslic3r/GCode/SeamPlacer.hpp | 2 +- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index 96b6dcd58..a54e55e00 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -512,14 +512,13 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, const orig_point = true; } - if (global_model_info.is_blocked(position, perimeter.flow_width)) { - type = EnforcedBlockedSeamPoint::Blocked; - } - if (global_model_info.is_enforced(position, perimeter.flow_width)) { type = EnforcedBlockedSeamPoint::Enforced; } + if (global_model_info.is_blocked(position, perimeter.flow_width)) { + type = EnforcedBlockedSeamPoint::Blocked; + } some_point_enforced = some_point_enforced || type == EnforcedBlockedSeamPoint::Enforced; if (orig_point) { @@ -828,7 +827,7 @@ struct SeamComparator { //avoid overhangs if ((a.overhang > 0.0f || b.overhang > 0.0f) - && abs(a.overhang - b.overhang) > (0.2f * a.perimeter.flow_width)) { + && abs(a.overhang - b.overhang) > (0.1f * a.perimeter.flow_width)) { return a.overhang < b.overhang; } @@ -1137,7 +1136,7 @@ void SeamPlacer::calculate_overhangs_and_layer_embedding(const PrintObject *po) * po->layers()[layer_idx]->height) / (3.0f * perimeter_point.perimeter.flow_width); //NOTE disables the feature to place seams on slowly decreasing areas. Remove the following line to enable. - perimeter_point.overhang = perimeter_point.overhang > 0.0f ? 0.0f : perimeter_point.overhang; + perimeter_point.overhang = perimeter_point.overhang < 0.0f ? 0.0f : perimeter_point.overhang; } if (should_compute_layer_embedding) { // search for embedded perimeter points (points hidden inside the print ,e.g. multimaterial join, best position for seam) @@ -1375,44 +1374,55 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: observation_points.resize(seam_string.size()); weights.resize(seam_string.size()); + auto angle_3d = [](const Vec3f& a, const Vec3f& b){ + return std::abs(acosf(a.normalized().dot(b.normalized()))); + }; + auto angle_weight = [](float angle){ return 1.0f / (0.1f + compute_angle_penalty(angle)); }; - float sharp_angle_weight = angle_weight(SeamPlacer::sharp_angle_snapping_threshold); //gather points positions and weights float total_length = 0.0f; - float sharp_length = 0.0f; - Vec3f prev_pos = layers[seam_string[0].first].points[seam_string[0].second].position; + Vec3f last_point_pos = layers[seam_string[0].first].points[seam_string[0].second].position; for (size_t index = 0; index < seam_string.size(); ++index) { - const SeamCandidate &point = layers[seam_string[index].first].points[seam_string[index].second]; - Vec3f pos = point.position; - float dist = (pos - prev_pos).norm(); - total_length += dist; - observations[index] = pos.head<2>(); - observation_points[index] = pos.z(); - weights[index] = angle_weight(point.local_ccw_angle); - bool is_enforced = point.type == EnforcedBlockedSeamPoint::Enforced; - if (is_enforced) { - weights[index] = std::max(weights[index], 6.0f); + const SeamCandidate ¤t = layers[seam_string[index].first].points[seam_string[index].second]; + float layer_angle = 0.0f; + if (index > 0 && index < seam_string.size() - 1) { + layer_angle = angle_3d( + current.position + - layers[seam_string[index - 1].first].points[seam_string[index - 1].second].position, + layers[seam_string[index + 1].first].points[seam_string[index + 1].second].position + - current.position + ); } - if (is_enforced || weights[index] > sharp_angle_weight) { - sharp_length+= dist; + observations[index] = current.position.head<2>(); + observation_points[index] = current.position.z(); + weights[index] = angle_weight(current.local_ccw_angle); + float sign = layer_angle > 2.0 * std::abs(current.local_ccw_angle) ? -1.0f : 1.0f; + if (current.type == EnforcedBlockedSeamPoint::Enforced) { + sign = 1.0f; + weights[index] += 3.0f; } - prev_pos = pos; + total_length += sign * (last_point_pos - current.position).norm(); + last_point_pos = current.position; } // Curve Fitting - size_t number_of_segments = std::max( - std::max(size_t(1), size_t(total_length / 30.0f)), - size_t(sharp_length / SeamPlacer::seam_align_mm_per_segment)); + size_t number_of_segments = std::max(size_t(1), + size_t(std::max(0.0f,total_length) / SeamPlacer::seam_align_mm_per_segment)); auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_segments); // Do alignment - compute fitted point for each point in the string from its Z coord, and store the position into // Perimeter structure of the point; also set flag aligned to true for (size_t index = 0; index < seam_string.size(); ++index) { const auto &pair = seam_string[index]; - const float t = std::min(1.0f, weights[index] / 10.0f); + float t = std::min(1.0f, std::abs(layers[pair.first].points[pair.second].local_ccw_angle) + / SeamPlacer::sharp_angle_snapping_threshold); + if (layers[pair.first].points[pair.second].type == EnforcedBlockedSeamPoint::Enforced){ + t = std::max(0.7f, t); + } + Vec3f current_pos = layers[pair.first].points[pair.second].position; Vec2f fitted_pos = curve.get_fitted_value(current_pos.z()); diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index c905fe8f6..4275e4942 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -131,7 +131,7 @@ public: // snapping angle - angles larger than this value will be snapped to during seam painting static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f; // overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction - static constexpr float overhang_angle_threshold = 50.0f * float(PI) / 180.0f; + static constexpr float overhang_angle_threshold = 45.0f * float(PI) / 180.0f; // determines angle importance compared to visibility ( neutral value is 1.0f. ) static constexpr float angle_importance_aligned = 0.6f;