fixes, central enforced point preference

This commit is contained in:
PavelMikus 2022-03-17 17:38:21 +01:00
parent 5c23d471de
commit 15135ef2ed
3 changed files with 65 additions and 16 deletions

View File

@ -409,6 +409,7 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std::
std::queue<Vec3f> oversampled_points { }; std::queue<Vec3f> oversampled_points { };
size_t orig_angle_index = 0; size_t orig_angle_index = 0;
perimeter->start_index = result_vec.size(); perimeter->start_index = result_vec.size();
bool some_point_enforced = false;
while (!orig_polygon_points.empty() || !oversampled_points.empty()) { while (!orig_polygon_points.empty() || !oversampled_points.empty()) {
EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral; EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::Neutral;
Vec3f position; 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)) { if (global_model_info.is_enforced(position, SeamPlacer::enforcer_blocker_distance_tolerance)) {
type = EnforcedBlockedSeamPoint::Enforced; type = EnforcedBlockedSeamPoint::Enforced;
some_point_enforced = true;
} }
if (global_model_info.is_blocked(position, SeamPlacer::enforcer_blocker_distance_tolerance)) { 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); result_vec.emplace_back(position, perimeter, local_ccw_angle, type);
} }
perimeter->end_index = result_vec.size() - 1; 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 // 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"; << "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 { struct SeamComparator {
SeamPosition setup; SeamPosition setup;
@ -625,6 +644,15 @@ struct SeamComparator {
// should return if a is better seamCandidate than b // 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, bool is_first_better(const SeamCandidate &a, const SeamCandidate &b, const Vec2f &preffered_location = Vec2f { 0.0f,
0.0f }) const { 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 // Blockers/Enforcers discrimination, top priority
if (a.type > b.type) { if (a.type > b.type) {
return true; 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 // 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 { bool is_first_not_much_worse(const SeamCandidate &a, const SeamCandidate &b) const {
// Blockers/Enforcers discrimination, top priority // 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) { if (a.type == EnforcedBlockedSeamPoint::Enforced) {
return true; return true;
} }
@ -972,6 +1009,13 @@ bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po,
SeamCandidate &next_layer_seam = SeamCandidate &next_layer_seam =
m_perimeter_points_per_object[po][layer_idx][closest_point.perimeter->seam_index]; 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<size_t, size_t> { layer_idx, closest_point.perimeter->seam_index };
return true;
}
auto are_similar = [&](const SeamCandidate &a, const SeamCandidate &b) { 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); 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(), std::sort(seams.begin(), seams.end(),
[&](const std::pair<size_t, size_t> &left, const std::pair<size_t, size_t> &right) { [&](const std::pair<size_t, size_t> &left, const std::pair<size_t, size_t> &right) {
return comparator.is_first_better(m_perimeter_points_per_object[po][left.first][left.second], 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) // gather all positions of seams and their weights (weights are derived as negative penalty, they are made positive in next step)
std::vector<Vec3f> observations(seam_string.size()); std::vector<Vec2f> observations(seam_string.size());
std::vector<float> observation_points(seam_string.size()); std::vector<float> observation_points(seam_string.size());
std::vector<float> weights(seam_string.size()); std::vector<float> 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) { for (size_t index = 0; index < seam_string.size(); ++index) {
Vec3f pos = Vec3f pos =
m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second].position; 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(); observation_points[index] = pos.z();
weights[index] = -comparator.get_penalty( weights[index] = -comparator.get_penalty(
m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second]); 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 // 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); 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 // 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 // Perimeter structure of the point; also set flag aligned to true
for (const auto &pair : seam_string) { for (const auto &pair : seam_string) {
Vec3f current_pos = m_perimeter_points_per_object[po][pair.first][pair.second].position; 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 = Perimeter *perimeter =
m_perimeter_points_per_object[po][pair.first][pair.second].perimeter.get(); 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; perimeter->finalized = true;
} }

View File

@ -56,7 +56,7 @@ struct SeamCandidate {
float local_ccw_angle, float local_ccw_angle,
EnforcedBlockedSeamPoint type) : 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), local_ccw_angle(
local_ccw_angle), type(type) { local_ccw_angle), type(type), central_enforcer(false){
} }
const Vec3f position; const Vec3f position;
// pointer to Perimter loop of this point. It is shared across all points of the loop // 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 overhang;
float local_ccw_angle; float local_ccw_angle;
EnforcedBlockedSeamPoint type; EnforcedBlockedSeamPoint type;
bool central_enforcer; //marks this candidate as central point of enforced segment on the perimeter - important for alignment
}; };
struct FaceVisibilityInfo { struct FaceVisibilityInfo {
@ -113,7 +114,7 @@ public:
// minimum number of seams needed in cluster to make alignemnt happen // minimum number of seams needed in cluster to make alignemnt happen
static constexpr size_t seam_align_minimum_string_seams = 6; static constexpr size_t seam_align_minimum_string_seams = 6;
// points covered by spline; determines number of splines for the given string // 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: //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 // Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter points of the given layer

View File

@ -119,7 +119,7 @@ struct PiecewiseFittedCurve {
// In other words, for function f(x) = y, observations are y0...yn, and observation points are x0...xn // In other words, for function f(x) = y, observations are y0...yn, and observation points are x0...xn
// weights: how important the observation is // weights: how important the observation is
// number_of_inner_splines: how many full inner splines are fit into the normalized valid range 0,1; // 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 // Kernel: model used for the curve fitting
template<int Dimension, typename NumberType, typename Kernel> template<int Dimension, typename NumberType, typename Kernel>
PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve( PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
@ -144,7 +144,7 @@ PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
assert(weights[index] > 0); assert(weights[index] > 0);
sqrt_weights[index + extremes_repetition] = sqrt(weights[index]); 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) { for (int index = 0; index < int(extremes_repetition); ++index) {
sqrt_weights[index] = sqrt(weights.front()); sqrt_weights[index] = sqrt(weights.front());
sqrt_weights[sqrt_weights.size() - index - 1] = sqrt(weights.back()); sqrt_weights[sqrt_weights.size() - index - 1] = sqrt(weights.back());
@ -153,8 +153,8 @@ PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
// prepare result and compute metadata // prepare result and compute metadata
PiecewiseFittedCurve<Dimension, NumberType, Kernel> result { }; PiecewiseFittedCurve<Dimension, NumberType, Kernel> result { };
NumberType original_len = observation_points.back() - observation_points.front(); NumberType orig_len = observation_points.back() - observation_points.front();
NumberType orig_segment_size = original_len / NumberType(number_of_inner_splines * Kernel::kernel_span); NumberType orig_segment_size = orig_len / NumberType(number_of_inner_splines * Kernel::kernel_span);
result.kernel = kernel; result.kernel = kernel;
result.start = observation_points.front() - extremes_repetition * orig_segment_size; result.start = observation_points.front() - extremes_repetition * orig_segment_size;
result.length = observation_points.back() + extremes_repetition * orig_segment_size - result.start; result.length = observation_points.back() + extremes_repetition * orig_segment_size - result.start;
@ -187,7 +187,7 @@ PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
* sqrt_weights[index + extremes_repetition]; * 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 (int index = 0; index < int(extremes_repetition); index++) {
for (size_t dim = 0; dim < Dimension; ++dim) { for (size_t dim = 0; dim < Dimension; ++dim) {
data_points[dim](index) = observations.front()(dim) * sqrt_weights[index]; data_points[dim](index) = observations.front()(dim) * sqrt_weights[index];
@ -196,7 +196,7 @@ PiecewiseFittedCurve<Dimension, NumberType, Kernel> 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); Eigen::MatrixXf T(normalized_obs_points.size(), result.segments_count);
for (size_t i = 0; i < normalized_obs_points.size(); ++i) { for (size_t i = 0; i < normalized_obs_points.size(); ++i) {
for (size_t j = 0; j < result.segments_count; ++j) { for (size_t j = 0; j < result.segments_count; ++j) {
@ -204,11 +204,14 @@ PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
} }
} }
//Fill the weight matrix
for (size_t i = 0; i < normalized_obs_points.size(); ++i) { for (size_t i = 0; i < normalized_obs_points.size(); ++i) {
NumberType knot_val = normalized_obs_points[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); 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); for (int segment_index = start_segment_idx; segment_index < int(start_segment_idx + Kernel::kernel_span);
segment_index++) { segment_index++) {
// skip if we overshoot segment_index - happens at the extremes
if (segment_index < 0 || segment_index >= int(result.segments_count)) { if (segment_index < 0 || segment_index >= int(result.segments_count)) {
continue; continue;
} }