mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-13 03:01:48 +08:00
alignment from best candidate
This commit is contained in:
parent
eccf1c1553
commit
2274965079
@ -2586,7 +2586,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||||||
loop.split_at(last_pos, false);
|
loop.split_at(last_pos, false);
|
||||||
}
|
}
|
||||||
else
|
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;
|
// 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
|
// if polyline was shorter than the clipping distance we'd get a null polyline, so
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
#include "Subdivide.hpp"
|
||||||
|
|
||||||
//For polynomial fitting
|
//For polynomial fitting
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
#include <Eigen/QR>
|
#include <Eigen/QR>
|
||||||
@ -24,7 +26,6 @@
|
|||||||
#define DEBUG_FILES
|
#define DEBUG_FILES
|
||||||
|
|
||||||
#ifdef DEBUG_FILES
|
#ifdef DEBUG_FILES
|
||||||
#include "Subdivide.hpp"
|
|
||||||
#include <boost/nowide/cstdio.hpp>
|
#include <boost/nowide/cstdio.hpp>
|
||||||
#include <SVG.hpp>
|
#include <SVG.hpp>
|
||||||
#endif
|
#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) {
|
for (size_t face_index = r.begin(); face_index < r.end(); ++face_index) {
|
||||||
FaceVisibilityInfo &dest = result[face_index];
|
FaceVisibilityInfo &dest = result[face_index];
|
||||||
dest.visibility = 1.0f;
|
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];
|
Vec3i face = triangles.indices[face_index];
|
||||||
Vec3f A = triangles.vertices[face.x()];
|
Vec3f A = triangles.vertices[face.x()];
|
||||||
@ -218,7 +218,7 @@ std::vector<FaceVisibilityInfo> raycast_visibility(const AABBTreeIndirect::Tree<
|
|||||||
igl::Hit hitpoint;
|
igl::Hit hitpoint;
|
||||||
// FIXME: This AABBTTreeIndirect query will not compile for float ray origin and
|
// FIXME: This AABBTTreeIndirect query will not compile for float ray origin and
|
||||||
// direction.
|
// 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>();
|
Vec3d final_ray_dir_d = final_ray_dir.cast<double>();
|
||||||
bool hit = AABBTreeIndirect::intersect_ray_first_hit(triangles.vertices,
|
bool hit = AABBTreeIndirect::intersect_ray_first_hit(triangles.vertices,
|
||||||
triangles.indices, raycasting_tree, ray_origin_d, final_ray_dir_d, hitpoint);
|
triangles.indices, raycasting_tree, ray_origin_d, final_ray_dir_d, hitpoint);
|
||||||
@ -312,7 +312,7 @@ struct GlobalModelInfo {
|
|||||||
if (enforcers.empty()) {
|
if (enforcers.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
float radius_sqr = radius*radius;
|
float radius_sqr = radius * radius;
|
||||||
return AABBTreeIndirect::is_any_triangle_in_radius(enforcers.vertices, enforcers.indices,
|
return AABBTreeIndirect::is_any_triangle_in_radius(enforcers.vertices, enforcers.indices,
|
||||||
enforcers_tree, position, radius_sqr);
|
enforcers_tree, position, radius_sqr);
|
||||||
}
|
}
|
||||||
@ -321,7 +321,7 @@ struct GlobalModelInfo {
|
|||||||
if (blockers.empty()) {
|
if (blockers.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
float radius_sqr = radius*radius;
|
float radius_sqr = radius * radius;
|
||||||
return AABBTreeIndirect::is_any_triangle_in_radius(blockers.vertices, blockers.indices,
|
return AABBTreeIndirect::is_any_triangle_in_radius(blockers.vertices, blockers.indices,
|
||||||
blockers_tree, position, radius_sqr);
|
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;
|
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,
|
// Computes all global model info - transforms object, performs raycasting,
|
||||||
// stores enforces and blockers
|
// stores enforces and blockers
|
||||||
void compute_global_occlusion(GlobalModelInfo &result, const PrintObject *po) {
|
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";
|
<< "SeamPlacer: build AABB tree for raycasting and gather occlusion info: start";
|
||||||
// Build AABB tree for raycasting
|
// Build AABB tree for raycasting
|
||||||
auto obj_transform = po->trafo();
|
auto obj_transform = po->trafo();
|
||||||
auto triangle_set = po->model_object()->raw_indexed_triangle_set();
|
indexed_triangle_set triangle_set;
|
||||||
//add model parts
|
//add all parts
|
||||||
for (const ModelVolume *model_volume : po->model_object()->volumes) {
|
for (const ModelVolume *model_volume : po->model_object()->volumes) {
|
||||||
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
|
if (model_volume->type() == ModelVolumeType::MODEL_PART) {
|
||||||
auto model_transformation = model_volume->get_matrix();
|
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);
|
its_merge(triangle_set, model_its);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float target_error = SeamPlacer::raycasting_decimation_target_error;
|
float target_error = SeamPlacer::raycasting_decimation_target_error;
|
||||||
its_quadric_edge_collapse(triangle_set, 0, &target_error, nullptr, nullptr);
|
its_quadric_edge_collapse(triangle_set, 0, &target_error, nullptr, nullptr);
|
||||||
triangle_set = subdivide(triangle_set, SeamPlacer::raycasting_subdivision_target_length);
|
triangle_set = subdivide(triangle_set, SeamPlacer::raycasting_subdivision_target_length);
|
||||||
@ -634,12 +614,19 @@ struct SeamComparator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float compute_angle_penalty(float ccw_angle) const {
|
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
|
// 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
|
// 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
|
// Blockers/Enforcers discrimination, top priority
|
||||||
if (a.type > b.type) {
|
if (a.type > b.type) {
|
||||||
return true;
|
return true;
|
||||||
@ -649,7 +636,7 @@ struct SeamComparator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//avoid overhangs
|
//avoid overhangs
|
||||||
if (a.overhang > 0.3f && b.overhang < a.overhang) {
|
if (a.overhang > 0.1f && b.overhang < a.overhang) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -657,8 +644,18 @@ struct SeamComparator {
|
|||||||
return a.position.y() > b.position.y();
|
return a.position.y() > b.position.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (a.visibility + 1.0f) * compute_angle_penalty(a.local_ccw_angle) <
|
float distance_penalty_a = 1.0f;
|
||||||
(b.visibility + 1.0f) * compute_angle_penalty(b.local_ccw_angle);
|
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
|
// 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
|
//avoid overhangs
|
||||||
if (a.overhang > 0.3f && b.overhang < a.overhang) {
|
if (a.overhang > 0.1f && b.overhang < a.overhang) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setup == SeamPosition::spRandom) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (setup == SeamPosition::spRear) {
|
if (setup == SeamPosition::spRear) {
|
||||||
return a.position.y() > b.position.y();
|
return a.position.y() > b.position.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (a.visibility + 1.0f) * compute_angle_penalty(a.local_ccw_angle) * 0.5f <=
|
//ranges: [0 - 1] (0 - 1.3] ; total range: (0 - 1.95];
|
||||||
(b.visibility + 1.0f) * compute_angle_penalty(b.local_ccw_angle);
|
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
|
//always nonzero, positive
|
||||||
float get_weight(const SeamCandidate &a) const {
|
float get_penalty(const SeamCandidate &a) const {
|
||||||
if (setup == SeamPosition::spRear) {
|
if (setup == SeamPosition::spRear) {
|
||||||
return a.position.y();
|
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);
|
min_vis = std::min(min_vis, point.visibility);
|
||||||
max_vis = std::max(max_vis, point.visibility);
|
max_vis = std::max(max_vis, point.visibility);
|
||||||
|
|
||||||
min_weight = std::min(min_weight, comparator.get_weight(point));
|
min_weight = std::min(min_weight, -comparator.get_penalty(point));
|
||||||
max_weight = std::max(max_weight, comparator.get_weight(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()) + ")";
|
+ std::to_string(color.z()) + ")";
|
||||||
visibility_svg.draw(scaled(Vec2f(point.position.head<2>())), visibility_fill);
|
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::string weight_fill = "rgb(" + std::to_string(weight_color.x()) + "," + std::to_string(weight_color.y())
|
||||||
+ ","
|
+ ","
|
||||||
+ std::to_string(weight_color.z()) + ")";
|
+ std::to_string(weight_color.z()) + ")";
|
||||||
@ -738,6 +742,106 @@ void debug_export_points(const std::vector<std::vector<SeamPlacerImpl::SeamCandi
|
|||||||
}
|
}
|
||||||
#endif
|
#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
|
} // namespace SeamPlacerImpl
|
||||||
|
|
||||||
// Parallel process and extract each perimeter polygon of the given print object.
|
// 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::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
|
||||||
[&](tbb::blocked_range<size_t> r) {
|
[&](tbb::blocked_range<size_t> r) {
|
||||||
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
|
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);
|
const Layer *layer = po->get_layer(layer_idx);
|
||||||
auto unscaled_z = layer->slice_z;
|
auto unscaled_z = layer->slice_z;
|
||||||
Polygons polygons = extract_perimeter_polygons(layer);
|
Polygons polygons = extract_perimeter_polygons(layer);
|
||||||
@ -762,8 +867,9 @@ void SeamPlacer::gather_seam_candidates(const PrintObject *po,
|
|||||||
global_model_info);
|
global_model_info);
|
||||||
}
|
}
|
||||||
auto functor = SeamCandidateCoordinateFunctor { &layer_candidates };
|
auto functor = SeamCandidateCoordinateFunctor { &layer_candidates };
|
||||||
m_perimeter_points_trees_per_object[po][layer_idx] = std::make_unique<SeamCandidatesTree>(
|
m_perimeter_points_trees_per_object[po][layer_idx] = std::make_unique<SeamCandidatesTree
|
||||||
functor, layer_candidates.size());
|
>(
|
||||||
|
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
|
// 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
|
// Otherwise does nothing, returns false
|
||||||
// sadly cannot be const because map access operator[] is not const, since it can create new object
|
// 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,
|
bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po,
|
||||||
std::pair<size_t, size_t> &last_point_indexes,
|
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>> &seam_string,
|
||||||
std::vector<std::pair<size_t, size_t>> &potential_string_seams) {
|
std::vector<std::pair<size_t, size_t>> &potential_string_seams) {
|
||||||
using namespace SeamPlacerImpl;
|
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];
|
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;
|
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
|
// Does not change the positions of the SeamCandidates themselves, instead stores
|
||||||
// the new aligned position into the shared Perimeter structure of each perimeter
|
// the new aligned position into the shared Perimeter structure of each perimeter
|
||||||
// Note that this position does not necesarilly lay on the perimeter.
|
// Note that this position does not necesarilly lay on the perimeter.
|
||||||
template<typename Comparator>
|
void SeamPlacer::align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator) {
|
||||||
void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comparator) {
|
|
||||||
using namespace SeamPlacerImpl;
|
using namespace SeamPlacerImpl;
|
||||||
|
|
||||||
// Prepares Debug files for writing.
|
// Prepares Debug files for writing.
|
||||||
@ -901,7 +1005,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
|
|||||||
}
|
}
|
||||||
#endif
|
#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;
|
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) {
|
for (size_t layer_idx = 0; layer_idx < m_perimeter_points_per_object[po].size(); ++layer_idx) {
|
||||||
std::vector<SeamCandidate> &layer_perimeter_points =
|
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) {
|
for (const std::pair<size_t, size_t> &seam : seams) {
|
||||||
size_t layer_idx = seam.first;
|
size_t layer_idx = seam.first;
|
||||||
size_t seam_index = seam.second;
|
size_t seam_index = seam.second;
|
||||||
std::vector<SeamCandidate> &layer_perimeter_points =
|
std::vector<SeamCandidate> &layer_perimeter_points =
|
||||||
m_perimeter_points_per_object[po][layer_idx];
|
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
|
// This perimeter is already aligned, skip seam
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
@ -937,7 +1041,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
|
|||||||
int next_layer = layer_idx + 1;
|
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::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;
|
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
|
//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) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// String is long engouh, all string seams and potential string seams gathered, now do the alignment
|
// 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());
|
seam_string.insert(seam_string.end(), potential_string_seams.begin(), potential_string_seams.end());
|
||||||
//sort by layer index
|
//sort by layer index
|
||||||
std::sort(seam_string.begin(), seam_string.end(),
|
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;
|
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<Vec3f> points(seam_string.size());
|
||||||
std::vector<float> weights(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]);
|
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) {
|
for (size_t index = 0; index < seam_string.size(); ++index) {
|
||||||
points[index] =
|
points[index] =
|
||||||
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;
|
||||||
weights[index] = comparator.get_weight(
|
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]);
|
||||||
min_weight = std::min(min_weight, weights[index]);
|
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) {
|
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.
|
//NOTE: the following commented block does polynomial line fitting of the seam string.
|
||||||
std::vector<Vec2f> coefficients = polyfit(points, weights, 4);
|
// pre-smoothen by Laplace
|
||||||
|
// for (size_t iteration = 0; iteration < SeamPlacer::seam_align_laplace_smoothing_iterations; ++iteration) {
|
||||||
// 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) {
|
|
||||||
// std::vector<Vec3f> new_points(seam_string.size());
|
// std::vector<Vec3f> new_points(seam_string.size());
|
||||||
// for (int point_index = 0; point_index < points.size(); ++point_index) {
|
// 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 prev_idx = point_index > 0 ? point_index - 1 : point_index;
|
||||||
// size_t next_idx = point_index < points.size() - 1 ? 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]
|
// new_points[point_index] = (points[prev_idx] * weights[prev_idx]
|
||||||
// +points[point_index] * weights[point_index]+ points[next_idx] * weights[next_idx]) /
|
// + points[point_index] * weights[point_index] + points[next_idx] * weights[next_idx]) /
|
||||||
// (weights[prev_idx] + weights[point_index]+ weights[next_idx]);
|
// (weights[prev_idx] + weights[point_index] + weights[next_idx]);
|
||||||
// }
|
// }
|
||||||
// points = new_points;
|
// 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 =
|
// Perimeter *perimeter =
|
||||||
// m_perimeter_points_per_object[po][seam_string[index].first][seam_string[index].second].perimeter.get();
|
// m_perimeter_points_per_object[po][pair.first][pair.second].perimeter.get();
|
||||||
// perimeter->final_seam_position = points[index];
|
// perimeter->final_seam_position = seam_pos;
|
||||||
// perimeter->aligned = true;
|
// 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
|
#ifdef DEBUG_FILES
|
||||||
auto randf = []() {
|
auto randf = []() {
|
||||||
@ -1085,7 +1228,7 @@ void SeamPlacer::init(const Print &print) {
|
|||||||
GlobalModelInfo global_model_info { };
|
GlobalModelInfo global_model_info { };
|
||||||
gather_enforcers_blockers(global_model_info, po);
|
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);
|
compute_global_occlusion(global_model_info, po);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1095,7 +1238,7 @@ void SeamPlacer::init(const Print &print) {
|
|||||||
BOOST_LOG_TRIVIAL(debug)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< "SeamPlacer: gather_seam_candidates: end";
|
<< "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)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< "SeamPlacer: calculate_candidates_visibility : start";
|
<< "SeamPlacer: calculate_candidates_visibility : start";
|
||||||
calculate_candidates_visibility(po, global_model_info);
|
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];
|
m_perimeter_points_per_object[po][layer_idx];
|
||||||
size_t current = 0;
|
size_t current = 0;
|
||||||
while (current < layer_perimeter_points.size()) {
|
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;
|
current = layer_perimeter_points[current].perimeter->end_index + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1127,7 +1274,7 @@ void SeamPlacer::init(const Print &print) {
|
|||||||
BOOST_LOG_TRIVIAL(debug)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< "SeamPlacer: pick_seam_point : end";
|
<< "SeamPlacer: pick_seam_point : end";
|
||||||
|
|
||||||
if (configured_seam_preference != spRandom) {
|
if (configured_seam_preference == spAligned) {
|
||||||
BOOST_LOG_TRIVIAL(debug)
|
BOOST_LOG_TRIVIAL(debug)
|
||||||
<< "SeamPlacer: align_seam_points : start";
|
<< "SeamPlacer: align_seam_points : start";
|
||||||
align_seam_points(po, comparator);
|
align_seam_points(po, comparator);
|
||||||
@ -1135,20 +1282,22 @@ void SeamPlacer::init(const Print &print) {
|
|||||||
<< "SeamPlacer: align_seam_points : end";
|
<< "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),
|
debug_export_points(m_perimeter_points_per_object[po], po->bounding_box(), std::to_string(po->id().id),
|
||||||
comparator);
|
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;
|
using namespace SeamPlacerImpl;
|
||||||
const PrintObject *po = layer->object();
|
const PrintObject *po = layer->object();
|
||||||
//NOTE this is necessary, since layer->id() is quite unreliable
|
//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()));
|
size_t layer_index = std::max(0, int(layer->id()) - int(po->slicing_parameters().raft_layers()));
|
||||||
double unscaled_z = layer->slice_z;
|
double unscaled_z = layer->slice_z;
|
||||||
|
|
||||||
const auto &perimeter_points_tree = *m_perimeter_points_trees_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[po][layer_index];
|
const auto &perimeter_points = m_perimeter_points_per_object.find(po)->second[layer_index];
|
||||||
|
|
||||||
const Point &fp = loop.first_point();
|
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,
|
size_t closest_perimeter_point_index = find_closest_point(perimeter_points_tree,
|
||||||
Vec3f { unscaled_p.x(), unscaled_p.y(), float(unscaled_z) });
|
Vec3f { unscaled_p.x(), unscaled_p.y(), float(unscaled_z) });
|
||||||
const Perimeter *perimeter = perimeter_points[closest_perimeter_point_index].perimeter.get();
|
const Perimeter *perimeter = perimeter_points[closest_perimeter_point_index].perimeter.get();
|
||||||
Vec3f seam_position = perimeter_points[perimeter->seam_index].position;
|
|
||||||
if (perimeter->aligned) {
|
size_t seam_index;
|
||||||
seam_position = perimeter->final_seam_position;
|
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() });
|
Point seam_point = scaled(Vec2d { seam_position.x(), seam_position.y() });
|
||||||
|
|
||||||
if (!loop.split_at_vertex(seam_point))
|
if (!loop.split_at_vertex(seam_point))
|
||||||
|
@ -27,6 +27,7 @@ class Grid;
|
|||||||
namespace SeamPlacerImpl {
|
namespace SeamPlacerImpl {
|
||||||
|
|
||||||
struct GlobalModelInfo;
|
struct GlobalModelInfo;
|
||||||
|
struct SeamComparator;
|
||||||
|
|
||||||
enum class EnforcedBlockedSeamPoint {
|
enum class EnforcedBlockedSeamPoint {
|
||||||
Blocked = 0,
|
Blocked = 0,
|
||||||
@ -40,9 +41,10 @@ struct Perimeter {
|
|||||||
size_t end_index; //inclusive!
|
size_t end_index; //inclusive!
|
||||||
size_t seam_index;
|
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
|
// 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;
|
Vec3f final_seam_position;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,20 +86,22 @@ class SeamPlacer {
|
|||||||
public:
|
public:
|
||||||
using SeamCandidatesTree =
|
using SeamCandidatesTree =
|
||||||
KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
|
KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
|
||||||
static constexpr float raycasting_decimation_target_error = 2.0f;
|
static constexpr float raycasting_decimation_target_error = 1.0f;
|
||||||
static constexpr float raycasting_subdivision_target_length = 3.0f;
|
static constexpr float raycasting_subdivision_target_length = 1.0f;
|
||||||
//square of number of rays per triangle
|
//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
|
// 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
|
// 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;
|
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
|
// 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:
|
// 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,
|
// 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
|
//it belongs automaticaly to the cluster
|
||||||
static constexpr float seam_align_tolerable_dist = 0.5f;
|
static constexpr float seam_align_tolerable_dist = 0.5f;
|
||||||
@ -106,6 +110,8 @@ public:
|
|||||||
static constexpr size_t seam_align_tolerable_skips = 4;
|
static constexpr size_t seam_align_tolerable_skips = 4;
|
||||||
// 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;
|
||||||
|
// 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:
|
//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
|
||||||
@ -115,19 +121,17 @@ public:
|
|||||||
|
|
||||||
void init(const Print &print);
|
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:
|
private:
|
||||||
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
|
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
|
||||||
void calculate_candidates_visibility(const PrintObject *po,
|
void calculate_candidates_visibility(const PrintObject *po,
|
||||||
const SeamPlacerImpl::GlobalModelInfo &global_model_info);
|
const SeamPlacerImpl::GlobalModelInfo &global_model_info);
|
||||||
void calculate_overhangs(const PrintObject *po);
|
void calculate_overhangs(const PrintObject *po);
|
||||||
template<typename Comparator>
|
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
|
||||||
void align_seam_points(const PrintObject *po, const Comparator &comparator);
|
|
||||||
template<typename Comparator>
|
|
||||||
bool find_next_seam_in_layer(const PrintObject *po,
|
bool find_next_seam_in_layer(const PrintObject *po,
|
||||||
std::pair<size_t, size_t> &last_point,
|
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>> &seam_strings,
|
||||||
std::vector<std::pair<size_t, size_t>> &outliers);
|
std::vector<std::pair<size_t, size_t>> &outliers);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user