fixes, central enforced point preference
This commit is contained in:
parent
5c23d471de
commit
15135ef2ed
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user