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 { };
|
||||
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<size_t, size_t> { 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<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],
|
||||
@ -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<Vec3f> observations(seam_string.size());
|
||||
std::vector<Vec2f> observations(seam_string.size());
|
||||
std::vector<float> observation_points(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) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<int Dimension, typename NumberType, typename Kernel>
|
||||
PiecewiseFittedCurve<Dimension, NumberType, Kernel> fit_curve(
|
||||
@ -144,7 +144,7 @@ PiecewiseFittedCurve<Dimension, NumberType, Kernel> 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<Dimension, NumberType, Kernel> fit_curve(
|
||||
// prepare result and compute metadata
|
||||
PiecewiseFittedCurve<Dimension, NumberType, Kernel> 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<Dimension, NumberType, Kernel> 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<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);
|
||||
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<Dimension, NumberType, Kernel> 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user