alignment from best candidate

This commit is contained in:
PavelMikus 2022-03-09 11:22:22 +01:00
parent eccf1c1553
commit 2274965079
3 changed files with 271 additions and 110 deletions

View file

@ -2586,7 +2586,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
loop.split_at(last_pos, false);
}
else
m_seam_placer.place_seam(m_layer, loop, m_config.external_perimeters_first);
m_seam_placer.place_seam(m_layer, loop, m_config.external_perimeters_first, this->last_pos());
// clip the path to avoid the extruder to get exactly on the first point of the loop;
// if polyline was shorter than the clipping distance we'd get a null polyline, so

View file

@ -8,6 +8,8 @@
#include <algorithm>
#include <queue>
#include "Subdivide.hpp"
//For polynomial fitting
#include <Eigen/Dense>
#include <Eigen/QR>
@ -24,7 +26,6 @@
#define DEBUG_FILES
#ifdef DEBUG_FILES
#include "Subdivide.hpp"
#include <boost/nowide/cstdio.hpp>
#include <SVG.hpp>
#endif
@ -200,8 +201,7 @@ std::vector<FaceVisibilityInfo> raycast_visibility(const AABBTreeIndirect::Tree<
for (size_t face_index = r.begin(); face_index < r.end(); ++face_index) {
FaceVisibilityInfo &dest = result[face_index];
dest.visibility = 1.0f;
constexpr float decrease = 1.0f / (SeamPlacer::sqr_rays_per_triangle * SeamPlacer::sqr_rays_per_triangle);
constexpr float decrease = 1.0f / (SeamPlacer::sqr_rays_per_triangle * SeamPlacer::sqr_rays_per_triangle);
Vec3i face = triangles.indices[face_index];
Vec3f A = triangles.vertices[face.x()];
@ -218,7 +218,7 @@ std::vector<FaceVisibilityInfo> raycast_visibility(const AABBTreeIndirect::Tree<
igl::Hit hitpoint;
// FIXME: This AABBTTreeIndirect query will not compile for float ray origin and
// direction.
Vec3d ray_origin_d = (center+0.1*normal).cast<double>();
Vec3d ray_origin_d = (center + normal).cast<double>(); // start one mm above surface.
Vec3d final_ray_dir_d = final_ray_dir.cast<double>();
bool hit = AABBTreeIndirect::intersect_ray_first_hit(triangles.vertices,
triangles.indices, raycasting_tree, ray_origin_d, final_ray_dir_d, hitpoint);
@ -312,7 +312,7 @@ struct GlobalModelInfo {
if (enforcers.empty()) {
return false;
}
float radius_sqr = radius*radius;
float radius_sqr = radius * radius;
return AABBTreeIndirect::is_any_triangle_in_radius(enforcers.vertices, enforcers.indices,
enforcers_tree, position, radius_sqr);
}
@ -321,7 +321,7 @@ struct GlobalModelInfo {
if (blockers.empty()) {
return false;
}
float radius_sqr = radius*radius;
float radius_sqr = radius * radius;
return AABBTreeIndirect::is_any_triangle_in_radius(blockers.vertices, blockers.indices,
blockers_tree, position, radius_sqr);
}
@ -535,27 +535,6 @@ float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_
return ((p - b).norm() + dist_ab + dist_bc) / 3.0;
}
// Pick best seam point based on the given comparator
template<typename Comparator>
void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index,
const Comparator &comparator) {
size_t end_index = perimeter_points[start_index].perimeter->end_index;
std::vector<size_t> indices(end_index + 1 - start_index);
for (size_t index = start_index; index <= end_index; ++index) {
indices[index - start_index] = index;
}
size_t seam_index = indices[0];
for (size_t index : indices) {
if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index])) {
seam_index = index;
}
}
perimeter_points[start_index].perimeter->seam_index = seam_index;
}
// Computes all global model info - transforms object, performs raycasting,
// stores enforces and blockers
void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po) {
@ -563,8 +542,8 @@ void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po) {
<< "SeamPlacer: build AABB tree for raycasting and gather occlusion info: start";
// Build AABB tree for raycasting
auto obj_transform = po->trafo();
auto triangle_set = po->model_object()->raw_indexed_triangle_set();
//add model parts
indexed_triangle_set triangle_set;
//add all parts
for (const ModelVolume *model_volume : po->model_object()->volumes) {
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
auto model_transformation = model_volume->get_matrix();
@ -573,6 +552,7 @@ void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po) {
its_merge(triangle_set, model_its);
}
}
float target_error = SeamPlacer::raycasting_decimation_target_error;
its_quadric_edge_collapse(triangle_set, 0, &target_error, nullptr, nullptr);
triangle_set = subdivide(triangle_set, SeamPlacer::raycasting_subdivision_target_length);
@ -634,12 +614,19 @@ struct SeamComparator {
}
float compute_angle_penalty(float ccw_angle) const {
return gauss(ccw_angle, 0.2f, 1.0f, 4.0f);
// This function is used:
// ((^(((1)/(x^(2)*3+1)))-1)/(-1))*1+((1)/(2+^(-x)))
// looks terribly, but it is gaussian combined with sigmoid,
// so that concave points have much smaller penalty over convex ones
return gauss(ccw_angle, 0.0f, 1.0f, 3.0f) +
1.0f / (2 + std::exp(-ccw_angle)); // sigmoid, which heavily favourizes concave angles
}
// Standard comparator, must respect the requirements of comparators (e.g. give same result on same inputs) for sorting usage
// should return if a is better seamCandidate than b
bool is_first_better(const SeamCandidate &a, const SeamCandidate &b) const {
bool is_first_better(const SeamCandidate &a, const SeamCandidate &b, const Vec2f &preffered_location = Vec2f { 0.0f,
0.0f }) const {
// Blockers/Enforcers discrimination, top priority
if (a.type > b.type) {
return true;
@ -649,7 +636,7 @@ struct SeamComparator {
}
//avoid overhangs
if (a.overhang > 0.3f && b.overhang < a.overhang) {
if (a.overhang > 0.1f && b.overhang < a.overhang) {
return false;
}
@ -657,8 +644,18 @@ struct SeamComparator {
return a.position.y() > b.position.y();
}
return (a.visibility + 1.0f) * compute_angle_penalty(a.local_ccw_angle) <
(b.visibility + 1.0f) * compute_angle_penalty(b.local_ccw_angle);
float distance_penalty_a = 1.0f;
float distance_penalty_b = 1.0f;
if (setup == spNearest) {
distance_penalty_a = 1.1f - gauss((a.position.head<2>() - preffered_location).norm(), 0.0f, 1.0f, 0.001f);
distance_penalty_b = 1.1f - gauss((a.position.head<2>() - preffered_location).norm(), 0.0f, 1.0f, 0.001f);
}
//ranges: [0 - 1] (0 - 1.3] [0.1 - 1.1) ; total range: (0 - 2.1]
float penalty_a = (a.visibility + 0.5f) * compute_angle_penalty(a.local_ccw_angle) * distance_penalty_a;
float penalty_b = (b.visibility + 0.5f) * compute_angle_penalty(b.local_ccw_angle) * distance_penalty_b;
return penalty_a < penalty_b;
}
// Comparator used during alignment. If there is close potential aligned point, it is comapred to the current
@ -673,25 +670,32 @@ struct SeamComparator {
}
//avoid overhangs
if (a.overhang > 0.3f && b.overhang < a.overhang) {
if (a.overhang > 0.1f && b.overhang < a.overhang) {
return false;
}
if (setup == SeamPosition::spRandom) {
return true;
}
if (setup == SeamPosition::spRear) {
return a.position.y() > b.position.y();
}
return (a.visibility + 1.0f) * compute_angle_penalty(a.local_ccw_angle) * 0.5f <=
(b.visibility + 1.0f) * compute_angle_penalty(b.local_ccw_angle);
//ranges: [0 - 1] (0 - 1.3] ; total range: (0 - 1.95];
float penalty_a = (a.visibility + 0.5f) * compute_angle_penalty(a.local_ccw_angle);
float penalty_b = (b.visibility + 0.5f) * compute_angle_penalty(b.local_ccw_angle);
return penalty_a <= penalty_b || std::abs(penalty_a - penalty_b) < SeamPlacer::seam_align_score_tolerance;
}
//returns negative value of penalties, should be nromalized against others in the same perimeter for use
float get_weight(const SeamCandidate &a) const {
//always nonzero, positive
float get_penalty(const SeamCandidate &a) const {
if (setup == SeamPosition::spRear) {
return a.position.y();
}
return -(a.visibility + 1.0f) * compute_angle_penalty(a.local_ccw_angle);
return (a.visibility + 0.5f) * compute_angle_penalty(a.local_ccw_angle);
}
}
;
@ -715,8 +719,8 @@ void debug_export_points(const std::vector<std::vector<SeamPlacerImpl::SeamCandi
min_vis = std::min(min_vis, point.visibility);
max_vis = std::max(max_vis, point.visibility);
min_weight = std::min(min_weight, comparator.get_weight(point));
max_weight = std::max(max_weight, comparator.get_weight(point));
min_weight = std::min(min_weight, -comparator.get_penalty(point));
max_weight = std::max(max_weight, -comparator.get_penalty(point));
}
@ -728,7 +732,7 @@ void debug_export_points(const std::vector<std::vector<SeamPlacerImpl::SeamCandi
+ std::to_string(color.z()) + ")";
visibility_svg.draw(scaled(Vec2f(point.position.head<2>())), visibility_fill);
Vec3i weight_color = value_rgbi(min_weight, max_weight, comparator.get_weight(point));
Vec3i weight_color = value_rgbi(min_weight, max_weight, comparator.get_penalty(point));
std::string weight_fill = "rgb(" + std::to_string(weight_color.x()) + "," + std::to_string(weight_color.y())
+ ","
+ std::to_string(weight_color.z()) + ")";
@ -738,6 +742,106 @@ void debug_export_points(const std::vector<std::vector<SeamPlacerImpl::SeamCandi
}
#endif
// Pick best seam point based on the given comparator
void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index,
const SeamComparator &comparator) {
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) {
if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index])) {
seam_index = index;
}
}
perimeter_points[start_index].perimeter->seam_index = seam_index;
}
size_t pick_nearest_seam_point_index(const std::vector<SeamCandidate> &perimeter_points, size_t start_index,
const Vec2f &preffered_location) {
size_t end_index = perimeter_points[start_index].perimeter->end_index;
SeamComparator comparator { spNearest };
size_t seam_index = start_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;
}
}
return seam_index;
}
// picks random seam point uniformly, respecting enforcers blockers and overhang avoidance.
void pick_random_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index) {
SeamComparator comparator { spRandom };
// algorithm keeps a list of viable points and their lengths. If it finds a point
// that is much better than the viable_example_index (e.g. better type, no overhang; see is_first_not_much_worse)
// then it throws away stored lists and starts from start
// in the end, he list should contain points with same type (Enforced > Neutral > Blocked) and also only those which are not
// big overhang.
size_t viable_example_index = start_index;
size_t end_index = perimeter_points[start_index].perimeter->end_index;
std::vector<size_t> viable_indices;
std::vector<float> viable_edges_lengths;
std::vector<Vec3f> viable_edges;
for (size_t index = start_index; index <= end_index; ++index) {
if (comparator.is_first_not_much_worse(perimeter_points[index], perimeter_points[viable_example_index]) &&
comparator.is_first_not_much_worse(perimeter_points[viable_example_index], perimeter_points[index])) {
// index ok, push info into respective vectors
Vec3f edge_to_next;
if (index == end_index) {
edge_to_next = (perimeter_points[start_index].position - perimeter_points[index].position);
} else
{
edge_to_next = (perimeter_points[index + 1].position - perimeter_points[index].position);
}
float dist_to_next = edge_to_next.norm();
viable_indices.push_back(index);
viable_edges_lengths.push_back(dist_to_next);
viable_edges.push_back(edge_to_next);
} else if (comparator.is_first_not_much_worse(perimeter_points[viable_example_index],
perimeter_points[index])) {
// index is worse then viable_example_index, skip this point
} else {
// index is better than viable example index, update example, clear gathered info, start again
// clear up all gathered info, start from scratch, update example index
viable_example_index = index;
viable_indices.clear();
viable_edges_lengths.clear();
viable_edges.clear();
Vec3f edge_to_next;
if (index == end_index) {
edge_to_next = (perimeter_points[start_index].position - perimeter_points[index].position);
} else {
edge_to_next = (perimeter_points[index + 1].position - perimeter_points[index].position);
}
float dist_to_next = edge_to_next.norm();
viable_indices.push_back(index);
viable_edges_lengths.push_back(dist_to_next);
viable_edges.push_back(edge_to_next);
}
}
// now pick random point from the stored options
float len_sum = std::accumulate(viable_edges_lengths.begin(), viable_edges_lengths.end(), 0.0f);
float picked_len = len_sum * (rand() / (float(RAND_MAX) + 1));
size_t point_idx = 0;
while (picked_len - viable_edges_lengths[point_idx] > 0) {
picked_len = picked_len - viable_edges_lengths[point_idx];
point_idx++;
}
Perimeter *perimeter = perimeter_points[start_index].perimeter.get();
perimeter->seam_index = viable_indices[point_idx];
perimeter->final_seam_position = perimeter_points[perimeter->seam_index].position
+ viable_edges[point_idx].normalized() * picked_len;
perimeter->finalized = true;
}
} // namespace SeamPlacerImpl
// Parallel process and extract each perimeter polygon of the given print object.
@ -753,7 +857,8 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po,
tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
[&](tbb::blocked_range<size_t> r) {
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
std::vector<SeamCandidate> &layer_candidates = m_perimeter_points_per_object[po][layer_idx];
std::vector<SeamCandidate> &layer_candidates =
m_perimeter_points_per_object[po][layer_idx];
const Layer *layer = po->get_layer(layer_idx);
auto unscaled_z = layer->slice_z;
Polygons polygons = extract_perimeter_polygons(layer);
@ -762,8 +867,9 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po,
global_model_info);
}
auto functor = SeamCandidateCoordinateFunctor { &layer_candidates };
m_perimeter_points_trees_per_object[po][layer_idx] = std::make_unique<SeamCandidatesTree>(
functor, layer_candidates.size());
m_perimeter_points_trees_per_object[po][layer_idx] = std::make_unique<SeamCandidatesTree
>(
functor, layer_candidates.size());
}
}
);
@ -823,10 +929,9 @@ void SeamPlacer::calculate_overhangs(const PrintObject *po) {
// If the closest point is good enough to replace current chosen seam, it is stored in potential_string_seams, returns true and updates last_point_pos
// Otherwise does nothing, returns false
// sadly cannot be const because map access operator[] is not const, since it can create new object
template<typename Comparator>
bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po,
std::pair<size_t, size_t> &last_point_indexes,
size_t layer_idx, const Comparator &comparator,
size_t layer_idx, const SeamPlacerImpl::SeamComparator &comparator,
std::vector<std::pair<size_t, size_t>> &seam_string,
std::vector<std::pair<size_t, size_t>> &potential_string_seams) {
using namespace SeamPlacerImpl;
@ -842,7 +947,7 @@ bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po,
SeamCandidate &closest_point = m_perimeter_points_per_object[po][layer_idx][closest_point_index];
if (closest_point.perimeter->aligned) { //already aligned, skip
if (closest_point.perimeter->finalized) { //already finalized, skip
return false;
}
@ -878,8 +983,7 @@ bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po,
// Does not change the positions of the SeamCandidates themselves, instead stores
// the new aligned position into the shared Perimeter structure of each perimeter
// Note that this position does not necesarilly lay on the perimeter.
template<typename Comparator>
void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comparator) {
void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator) {
using namespace SeamPlacerImpl;
// Prepares Debug files for writing.
@ -901,7 +1005,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
}
#endif
//gahter vector of all seams on the print_object - pair of layer_index and seam__index within that layer
//gather vector of all seams on the print_object - pair of layer_index and seam__index within that layer
std::vector<std::pair<size_t, size_t>> seams;
for (size_t layer_idx = 0; layer_idx < m_perimeter_points_per_object[po].size(); ++layer_idx) {
std::vector<SeamCandidate> &layer_perimeter_points =
@ -921,13 +1025,13 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
}
);
//align the sema points - start with the best, and check if they are aligned, if yes, skip, else start alignment
//align the seam points - start with the best, and check if they are aligned, if yes, skip, else start alignment
for (const std::pair<size_t, size_t> &seam : seams) {
size_t layer_idx = seam.first;
size_t seam_index = seam.second;
std::vector<SeamCandidate> &layer_perimeter_points =
m_perimeter_points_per_object[po][layer_idx];
if (layer_perimeter_points[seam_index].perimeter->aligned) {
if (layer_perimeter_points[seam_index].perimeter->finalized) {
// This perimeter is already aligned, skip seam
continue;
} else {
@ -937,7 +1041,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
int next_layer = layer_idx + 1;
std::pair<size_t, size_t> last_point_indexes = std::pair<size_t, size_t>(layer_idx, seam_index);
std::vector<std::pair<size_t, size_t>> seam_string;
std::vector<std::pair<size_t, size_t>> seam_string { std::pair<size_t, size_t>(layer_idx, seam_index) };
std::vector<std::pair<size_t, size_t>> potential_string_seams;
//find seams or potential seams in forward direction; there is a budget of skips allowed
@ -969,12 +1073,12 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
}
if (seam_string.size() + potential_string_seams.size() < seam_align_minimum_string_seams) {
//string long enough to be worth aligning, skip
//string NOT long enough to be worth aligning, skip
continue;
}
// String is long engouh, all string seams and potential string seams gathered, now do the alignment
// first merge potential_string_seams and seam_string; from now on, they all will be aligned
// first merge potential_string_seams and seam_string
seam_string.insert(seam_string.end(), potential_string_seams.begin(), potential_string_seams.end());
//sort by layer index
std::sort(seam_string.begin(), seam_string.end(),
@ -982,62 +1086,101 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
return left.first < right.first;
});
// gather all positions of seams and their weights
// gather all positions of seams and their weights (weights are derived as negative penalty, they are made positive in next step)
std::vector<Vec3f> points(seam_string.size());
std::vector<float> weights(seam_string.size());
float min_weight = comparator.get_weight(
//init min_weight by the first point
float min_weight = -comparator.get_penalty(
m_perimeter_points_per_object[po][seam_string[0].first][seam_string[0].second]);
// In the sorted seam_string array, point which started the alignment - the best candidate
size_t best_candidate_point_index = 0;
//gather points positions and weights, update min_weight in each step, and find the best candidate
for (size_t index = 0; index < seam_string.size(); ++index) {
points[index] =
m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second].position;
weights[index] = comparator.get_weight(
weights[index] = -comparator.get_penalty(
m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second]);
min_weight = std::min(min_weight, weights[index]);
// find the best candidate by comparing the layer indexes
if (seam_string[index].first == layer_idx) {
best_candidate_point_index = index;
}
}
//makes all weights positive
for (float &w : weights) {
w = w - min_weight + 1.0; //makes all weights positive, nonzero
w = w - min_weight + 0.01;
}
// find coefficients of polynomial fit. Z coord is treated as parameter along which to fit both X and Y coords.
std::vector<Vec2f> coefficients = polyfit(points, weights, 4);
// 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) {
float current_height = m_perimeter_points_per_object[po][pair.first][pair.second].position.z();
Vec3f seam_pos = get_fitted_point(coefficients, current_height);
Perimeter *perimeter =
m_perimeter_points_per_object[po][pair.first][pair.second].perimeter.get();
perimeter->final_seam_position = seam_pos;
perimeter->aligned = true;
}
for (Vec3f &p : points) {
p = get_fitted_point(coefficients, p.z());
}
// for (size_t iteration = 0; iteration < 20; ++iteration) {
//NOTE: the following commented block does polynomial line fitting of the seam string.
// pre-smoothen by Laplace
// for (size_t iteration = 0; iteration < SeamPlacer::seam_align_laplace_smoothing_iterations; ++iteration) {
// std::vector<Vec3f> new_points(seam_string.size());
// for (int point_index = 0; point_index < points.size(); ++point_index) {
// size_t prev_idx = point_index > 0 ? point_index - 1 : point_index;
// size_t next_idx = point_index < points.size() - 1 ? point_index + 1 : point_index;
//
// new_points[point_index] = (points[prev_idx] * weights[prev_idx]
// +points[point_index] * weights[point_index]+ points[next_idx] * weights[next_idx]) /
// (weights[prev_idx] + weights[point_index]+ weights[next_idx]);
// + points[point_index] * weights[point_index] + points[next_idx] * weights[next_idx]) /
// (weights[prev_idx] + weights[point_index] + weights[next_idx]);
// }
// points = new_points;
// }
//
// for (size_t index = 0; index < seam_string.size(); ++index) {
// // find coefficients of polynomial fit. Z coord is treated as parameter along which to fit both X and Y coords.
// std::vector<Vec2f> coefficients = polyfit(points, weights, 4);
//
// // 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) {
// float current_height = m_perimeter_points_per_object[po][pair.first][pair.second].position.z();
// Vec3f seam_pos = get_fitted_point(coefficients, current_height);
//
// Perimeter *perimeter =
// m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second].perimeter.get();
// perimeter->final_seam_position = points[index];
// perimeter->aligned = true;
// m_perimeter_points_per_object[po][pair.first][pair.second].perimeter.get();
// perimeter->final_seam_position = seam_pos;
// perimeter->finalized = true;
// }
//
// for (Vec3f &p : points) {
// p = get_fitted_point(coefficients, p.z());
// }
// LaPlace smoothing iterations over the gathered points. New positions from each iteration are stored in the new_points vector
// and assigned to points at the end of iteration
for (size_t iteration = 0; iteration < SeamPlacer::seam_align_laplace_smoothing_iterations; ++iteration) {
std::vector<Vec3f> new_points(seam_string.size());
// start from the best candidate, and smoothen down
for (int point_index = best_candidate_point_index; point_index >= 0; --point_index) {
int prev_idx = point_index > 0 ? point_index - 1 : point_index;
size_t next_idx = point_index < int(points.size()) - 1 ? point_index + 1 : point_index;
new_points[point_index] = (points[prev_idx] * weights[prev_idx]
+ points[point_index] * weights[point_index] + points[next_idx] * weights[next_idx]) /
(weights[prev_idx] + weights[point_index] + weights[next_idx]);
}
// smoothen up the rest of the points
for (size_t point_index = best_candidate_point_index + 1; point_index < points.size(); ++point_index) {
size_t prev_idx = point_index > 0 ? point_index - 1 : point_index;
size_t next_idx = point_index < points.size() - 1 ? point_index + 1 : point_index;
new_points[point_index] = (points[prev_idx] * weights[prev_idx]
+ points[point_index] * weights[point_index] + points[next_idx] * weights[next_idx]) /
(weights[prev_idx] + weights[point_index] + weights[next_idx]);
}
points = new_points;
}
// Assign smoothened posiiton to each participating perimeter and set finalized flag
for (size_t index = 0; index < seam_string.size(); ++index) {
Perimeter *perimeter =
m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second].perimeter.get();
perimeter->final_seam_position = points[index];
perimeter->finalized = true;
}
#ifdef DEBUG_FILES
auto randf = []() {
@ -1085,7 +1228,7 @@ void SeamPlacer::init(const Print &print) {
GlobalModelInfo global_model_info { };
gather_enforcers_blockers(global_model_info, po);
if (configured_seam_preference == spNearest || configured_seam_preference == spRandom) {
if (configured_seam_preference == spAligned || configured_seam_preference == spNearest) {
compute_global_occlusion(global_model_info, po);
}
@ -1095,7 +1238,7 @@ void SeamPlacer::init(const Print &print) {
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: gather_seam_candidates: end";
if (configured_seam_preference == spNearest || configured_seam_preference == spRandom) {
if (configured_seam_preference == spAligned || configured_seam_preference == spNearest) {
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: calculate_candidates_visibility : start";
calculate_candidates_visibility(po, global_model_info);
@ -1119,7 +1262,11 @@ void SeamPlacer::init(const Print &print) {
m_perimeter_points_per_object[po][layer_idx];
size_t current = 0;
while (current < layer_perimeter_points.size()) {
pick_seam_point(layer_perimeter_points, current, comparator);
if (configured_seam_preference == spRandom) {
pick_random_seam_point(layer_perimeter_points, current);
} else {
pick_seam_point(layer_perimeter_points, current, comparator);
}
current = layer_perimeter_points[current].perimeter->end_index + 1;
}
}
@ -1127,7 +1274,7 @@ void SeamPlacer::init(const Print &print) {
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: pick_seam_point : end";
if (configured_seam_preference != spRandom) {
if (configured_seam_preference == spAligned) {
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: align_seam_points : start";
align_seam_points(po, comparator);
@ -1135,20 +1282,22 @@ void SeamPlacer::init(const Print &print) {
<< "SeamPlacer: align_seam_points : end";
}
#ifdef DEBUG_FILES
debug_export_points(m_perimeter_points_per_object[po], po->bounding_box(), std::to_string(po->id().id),
comparator);
#endif
}
}
void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first) {
void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const {
using namespace SeamPlacerImpl;
const PrintObject *po = layer->object();
//NOTE this is necessary, since layer->id() is quite unreliable
size_t layer_index = std::max(0, int(layer->id()) - int(po->slicing_parameters().raft_layers()));
double unscaled_z = layer->slice_z;
const auto &perimeter_points_tree = *m_perimeter_points_trees_per_object[po][layer_index];
const auto &perimeter_points = m_perimeter_points_per_object[po][layer_index];
const auto &perimeter_points_tree = *m_perimeter_points_trees_per_object.find(po)->second[layer_index];
const auto &perimeter_points = m_perimeter_points_per_object.find(po)->second[layer_index];
const Point &fp = loop.first_point();
@ -1156,11 +1305,19 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern
size_t closest_perimeter_point_index = find_closest_point(perimeter_points_tree,
Vec3f { unscaled_p.x(), unscaled_p.y(), float(unscaled_z) });
const Perimeter *perimeter = perimeter_points[closest_perimeter_point_index].perimeter.get();
Vec3f seam_position = perimeter_points[perimeter->seam_index].position;
if (perimeter->aligned) {
seam_position = perimeter->final_seam_position;
size_t seam_index;
if (po->config().seam_position == spNearest) {
seam_index = pick_nearest_seam_point_index(perimeter_points, perimeter->start_index,
unscale(last_pos).cast<float>());
} else {
seam_index = perimeter->seam_index;
}
Vec3f seam_position = perimeter_points[seam_index].position;
if (perimeter->finalized) {
seam_position = perimeter->final_seam_position;
}
Point seam_point = scaled(Vec2d { seam_position.x(), seam_position.y() });
if (!loop.split_at_vertex(seam_point))

View file

@ -27,6 +27,7 @@ class Grid;
namespace SeamPlacerImpl {
struct GlobalModelInfo;
struct SeamComparator;
enum class EnforcedBlockedSeamPoint {
Blocked = 0,
@ -40,9 +41,10 @@ struct Perimeter {
size_t end_index; //inclusive!
size_t seam_index;
// During alignment, a final position may be stored here. In that case, aligned is set to true.
// 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
bool aligned = false;
// Random position also uses this flexibility to set final seam point position
bool finalized = false;
Vec3f final_seam_position;
};
@ -84,20 +86,22 @@ class SeamPlacer {
public:
using SeamCandidatesTree =
KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
static constexpr float raycasting_decimation_target_error = 2.0f;
static constexpr float raycasting_subdivision_target_length = 3.0f;
static constexpr float raycasting_decimation_target_error = 1.0f;
static constexpr float raycasting_subdivision_target_length = 1.0f;
//square of number of rays per triangle
static constexpr size_t sqr_rays_per_triangle = 8;
static constexpr size_t sqr_rays_per_triangle = 7;
// arm length used during angles computation
static constexpr float polygon_local_angles_arm_distance = 0.4f;
static constexpr float polygon_local_angles_arm_distance = 0.5f;
// If enforcer or blocker is closer to the seam candidate than this limit, the seam candidate is set to Blocker or Enforcer
static constexpr float enforcer_blocker_distance_tolerance = 0.3f;
// For long polygon sides, if they are close to the custom seam drawings, they are oversampled with this step size
static constexpr float enforcer_blocker_oversampling_distance = 0.3f;
static constexpr float enforcer_blocker_oversampling_distance = 0.1f;
// 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.6f;
// seam_align_tolerable_dist - if seam is closer to the previous seam position projected to the current layer than this value,
//it belongs automaticaly to the cluster
static constexpr float seam_align_tolerable_dist = 0.5f;
@ -106,6 +110,8 @@ public:
static constexpr size_t seam_align_tolerable_skips = 4;
// minimum number of seams needed in cluster to make alignemnt happen
static constexpr size_t seam_align_minimum_string_seams = 6;
// iterations of laplace smoothing
static constexpr size_t seam_align_laplace_smoothing_iterations = 20;
//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
@ -115,19 +121,17 @@ public:
void init(const Print &print);
void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first);
void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point& last_pos) const;
private:
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_candidates_visibility(const PrintObject *po,
const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_overhangs(const PrintObject *po);
template<typename Comparator>
void align_seam_points(const PrintObject *po, const Comparator &comparator);
template<typename Comparator>
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
bool find_next_seam_in_layer(const PrintObject *po,
std::pair<size_t, size_t> &last_point,
size_t layer_idx, const Comparator &comparator,
size_t layer_idx,const SeamPlacerImpl::SeamComparator &comparator,
std::vector<std::pair<size_t, size_t>> &seam_strings,
std::vector<std::pair<size_t, size_t>> &outliers);
};