From 15135ef2ed35f7d65fba6150af4bfb683e303e22 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 17 Mar 2022 17:38:21 +0100 Subject: [PATCH] fixes, central enforced point preference --- src/libslic3r/GCode/SeamPlacer.cpp | 61 ++++++++++++++++++++++++++---- src/libslic3r/GCode/SeamPlacer.hpp | 5 ++- src/libslic3r/Geometry/Curves.hpp | 15 +++++--- 3 files changed, 65 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/GCode/SeamPlacer.cpp b/src/libslic3r/GCode/SeamPlacer.cpp index b0254762f..d466941c0 100644 --- a/src/libslic3r/GCode/SeamPlacer.cpp +++ b/src/libslic3r/GCode/SeamPlacer.cpp @@ -409,6 +409,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(); + bool some_point_enforced = false; while (!orig_polygon_points.empty() || !oversampled_points.empty()) { EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral; Vec3f position; @@ -427,6 +428,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std:: if (global_model_info.is_enforced(position, SeamPlacer::enforcer_blocker_distance_tolerance)) { type = EnforcedBlockedSeamPoint::Enforced; + some_point_enforced = true; } if (global_model_info.is_blocked(position, SeamPlacer::enforcer_blocker_distance_tolerance)) { @@ -449,10 +451,28 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std:: } result_vec.emplace_back(position, perimeter, local_ccw_angle, type); - } perimeter->end_index = result_vec.size() - 1; + + // We will find first patch of enforced points (patch: continous section of enforced points) and select the middle + // point, which will have priority during alignemnt + // If there are multiple enforced patches in the perimeter, others are ignored + if (some_point_enforced) { + size_t first_enforced_idx = perimeter->start_index; + while (first_enforced_idx <= perimeter->end_index + && result_vec[first_enforced_idx].type != EnforcedBlockedSeamPoint::Enforced) { + first_enforced_idx++; + } + size_t last_enforced_idx = first_enforced_idx; + while (last_enforced_idx < perimeter->end_index + && result_vec[last_enforced_idx + 1].type == EnforcedBlockedSeamPoint::Enforced) { + last_enforced_idx++; + } + size_t central_idx = (first_enforced_idx + last_enforced_idx) / 2; + result_vec[central_idx].central_enforcer = true; + } + } // Get index of previous and next perimeter point of the layer. Because SeamCandidates of all polygons of the given layer @@ -603,7 +623,6 @@ void gather_enforcers_blockers(GlobalModelInfo &result, const PrintObject *po) { << "SeamPlacer: build AABB trees for raycasting enforcers/blockers: end"; } -//Comparator of seam points. It has two necessary methods: is_first_better and is_first_not_much_worse struct SeamComparator { SeamPosition setup; @@ -625,6 +644,15 @@ struct SeamComparator { // should return if a is better seamCandidate than b bool is_first_better(const SeamCandidate &a, const SeamCandidate &b, const Vec2f &preffered_location = Vec2f { 0.0f, 0.0f }) const { + if (setup == SeamPosition::spAligned && a.central_enforcer != b.central_enforcer) { + if (a.central_enforcer) { + return true; + } + if (b.central_enforcer) { + return false; + } + } + // Blockers/Enforcers discrimination, top priority if (a.type > b.type) { return true; @@ -664,6 +692,15 @@ struct SeamComparator { // sema point of the perimeter, to find out if the aligned point is not much worse than the current seam bool is_first_not_much_worse(const SeamCandidate &a, const SeamCandidate &b) const { // Blockers/Enforcers discrimination, top priority + if (setup == SeamPosition::spAligned && a.central_enforcer != b.central_enforcer) { + if (a.central_enforcer) { + return true; + } + if (b.central_enforcer) { + return false; + } + } + if (a.type == EnforcedBlockedSeamPoint::Enforced) { return true; } @@ -972,6 +1009,13 @@ bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po, SeamCandidate &next_layer_seam = m_perimeter_points_per_object[po][layer_idx][closest_point.perimeter->seam_index]; + if (next_layer_seam.central_enforcer + && (next_layer_seam.position - projected_position).norm() < 3 * SeamPlacer::seam_align_tolerable_dist) { + seam_string.push_back( { layer_idx, closest_point.perimeter->seam_index }); + last_point_indexes = std::pair { layer_idx, closest_point.perimeter->seam_index }; + return true; + } + auto are_similar = [&](const SeamCandidate &a, const SeamCandidate &b) { return comparator.is_first_not_much_worse(a, b) && comparator.is_first_not_much_worse(b, a); }; @@ -1027,7 +1071,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: } } - //sort them before alignment. Alignment is sensitive to intitializaion, this gives it better chance to choose something nice + //sort them before alignment. Alignment is sensitive to initializaion, this gives it better chance to choose something nice std::sort(seams.begin(), seams.end(), [&](const std::pair &left, const std::pair &right) { return comparator.is_first_better(m_perimeter_points_per_object[po][left.first][left.second], @@ -1091,7 +1135,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: }); // gather all positions of seams and their weights (weights are derived as negative penalty, they are made positive in next step) - std::vector observations(seam_string.size()); + std::vector observations(seam_string.size()); std::vector observation_points(seam_string.size()); std::vector weights(seam_string.size()); @@ -1103,7 +1147,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: for (size_t index = 0; index < seam_string.size(); ++index) { Vec3f pos = m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second].position; - observations[index] = pos; + observations[index] = pos.head<2>(); observation_points[index] = pos.z(); weights[index] = -comparator.get_penalty( m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second]); @@ -1116,18 +1160,19 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl:: } // Curve Fitting - size_t number_of_splines = std::max(size_t(1), size_t(observations.size() / SeamPlacer::seam_align_seams_per_spline)); + size_t number_of_splines = std::max(size_t(1), + size_t(observations.size() / SeamPlacer::seam_align_seams_per_spline)); auto curve = Geometry::fit_cubic_bspline(observations, observation_points, weights, number_of_splines); // 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 (const auto &pair : seam_string) { Vec3f current_pos = m_perimeter_points_per_object[po][pair.first][pair.second].position; - Vec3f seam_pos = curve.get_fitted_value(current_pos.z()); + Vec2f fitted_pos = curve.get_fitted_value(current_pos.z()); Perimeter *perimeter = m_perimeter_points_per_object[po][pair.first][pair.second].perimeter.get(); - perimeter->final_seam_position = seam_pos; + perimeter->final_seam_position = Vec3f { fitted_pos.x(), fitted_pos.y(), current_pos.z() }; perimeter->finalized = true; } diff --git a/src/libslic3r/GCode/SeamPlacer.hpp b/src/libslic3r/GCode/SeamPlacer.hpp index f42243448..939d6ec75 100644 --- a/src/libslic3r/GCode/SeamPlacer.hpp +++ b/src/libslic3r/GCode/SeamPlacer.hpp @@ -56,7 +56,7 @@ struct SeamCandidate { float local_ccw_angle, EnforcedBlockedSeamPoint type) : position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), local_ccw_angle( - local_ccw_angle), type(type) { + local_ccw_angle), type(type), central_enforcer(false){ } const Vec3f position; // pointer to Perimter loop of this point. It is shared across all points of the loop @@ -65,6 +65,7 @@ struct SeamCandidate { float overhang; float local_ccw_angle; EnforcedBlockedSeamPoint type; + bool central_enforcer; //marks this candidate as central point of enforced segment on the perimeter - important for alignment }; struct FaceVisibilityInfo { @@ -113,7 +114,7 @@ public: // minimum number of seams needed in cluster to make alignemnt happen static constexpr size_t seam_align_minimum_string_seams = 6; // points covered by spline; determines number of splines for the given string - static constexpr size_t seam_align_seams_per_spline = 10; + static constexpr size_t seam_align_seams_per_spline = 30; //The following data structures hold all perimeter points for all PrintObject. The structure is as follows: // Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter points of the given layer diff --git a/src/libslic3r/Geometry/Curves.hpp b/src/libslic3r/Geometry/Curves.hpp index d221bfd1f..b92ad2c36 100644 --- a/src/libslic3r/Geometry/Curves.hpp +++ b/src/libslic3r/Geometry/Curves.hpp @@ -119,7 +119,7 @@ struct PiecewiseFittedCurve { // In other words, for function f(x) = y, observations are y0...yn, and observation points are x0...xn // weights: how important the observation is // number_of_inner_splines: how many full inner splines are fit into the normalized valid range 0,1; -// final number of knots is Kernel::kernel_span times larger + additional segments on start and end +// final number of knots is Kernel::kernel_span times number_of_inner_splines + additional segments on start and end // Kernel: model used for the curve fitting template PiecewiseFittedCurve fit_curve( @@ -144,7 +144,7 @@ PiecewiseFittedCurve fit_curve( assert(weights[index] > 0); sqrt_weights[index + extremes_repetition] = sqrt(weights[index]); } - //repeat weights for addtional segments + //repeat weights for addtional extreme segments for (int index = 0; index < int(extremes_repetition); ++index) { sqrt_weights[index] = sqrt(weights.front()); sqrt_weights[sqrt_weights.size() - index - 1] = sqrt(weights.back()); @@ -153,8 +153,8 @@ PiecewiseFittedCurve fit_curve( // prepare result and compute metadata PiecewiseFittedCurve result { }; - NumberType original_len = observation_points.back() - observation_points.front(); - NumberType orig_segment_size = original_len / NumberType(number_of_inner_splines * Kernel::kernel_span); + NumberType orig_len = observation_points.back() - observation_points.front(); + NumberType orig_segment_size = orig_len / NumberType(number_of_inner_splines * Kernel::kernel_span); result.kernel = kernel; result.start = observation_points.front() - extremes_repetition * orig_segment_size; result.length = observation_points.back() + extremes_repetition * orig_segment_size - result.start; @@ -187,7 +187,7 @@ PiecewiseFittedCurve fit_curve( * sqrt_weights[index + extremes_repetition]; } } - //duplicate observed data at the start and end + //duplicate observed data at the extremes for (int index = 0; index < int(extremes_repetition); index++) { for (size_t dim = 0; dim < Dimension; ++dim) { data_points[dim](index) = observations.front()(dim) * sqrt_weights[index]; @@ -196,7 +196,7 @@ PiecewiseFittedCurve fit_curve( } } - //Create weight matrix for each point and each segment. + //Create weight matrix T for each point and each segment; Eigen::MatrixXf T(normalized_obs_points.size(), result.segments_count); for (size_t i = 0; i < normalized_obs_points.size(); ++i) { for (size_t j = 0; j < result.segments_count; ++j) { @@ -204,11 +204,14 @@ PiecewiseFittedCurve fit_curve( } } + //Fill the weight matrix for (size_t i = 0; i < normalized_obs_points.size(); ++i) { NumberType knot_val = normalized_obs_points[i]; + //find index of first segment that is affected by the point i; this can be deduced from kernel_span int start_segment_idx = int(floor(knot_val / result.n_segment_size)) - int(Kernel::kernel_span * 0.5f - 1.0f); for (int segment_index = start_segment_idx; segment_index < int(start_segment_idx + Kernel::kernel_span); segment_index++) { + // skip if we overshoot segment_index - happens at the extremes if (segment_index < 0 || segment_index >= int(result.segments_count)) { continue; }