implemented seam alignment using exponential smoothing

This commit is contained in:
PavelMikus 2022-02-24 17:56:27 +01:00
parent 596bd68f18
commit 38a9d870c0
2 changed files with 297 additions and 150 deletions

View File

@ -168,47 +168,46 @@ std::vector<HitInfo> raycast_visibility(const AABBTreeIndirect::Tree<3, float> &
} }
std::vector<float> calculate_polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths, std::vector<float> calculate_polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths,
float min_arm_length) float min_arm_length) {
{ if (polygon.size() == 1) {
assert(polygon.points.size() + 1 == lengths.size()); return {0.0f};
if (min_arm_length > 0.25f * lengths.back()) }
min_arm_length = 0.25f * lengths.back();
// Find the initial prev / next point span. std::vector<float> result(polygon.size());
size_t idx_prev = polygon.points.size();
size_t idx_curr = 0;
size_t idx_next = 1;
while (idx_prev > idx_curr && lengths.back() - lengths[idx_prev] < min_arm_length)
--idx_prev;
while (idx_next < idx_prev && lengths[idx_next] < min_arm_length)
++idx_next;
std::vector<float> angles(polygon.points.size(), 0.f); auto make_idx_circular = [&](int index) {
for (; idx_curr < polygon.points.size(); ++idx_curr) { while (index < 0) {
// Move idx_prev up until the distance between idx_prev and idx_curr is lower than min_arm_length. index += polygon.size();
if (idx_prev >= idx_curr) {
while (idx_prev < polygon.points.size()
&& lengths.back() - lengths[idx_prev] + lengths[idx_curr] > min_arm_length)
++idx_prev;
if (idx_prev == polygon.points.size())
idx_prev = 0;
} }
while (idx_prev < idx_curr && lengths[idx_curr] - lengths[idx_prev] > min_arm_length) return index % polygon.size();
++idx_prev; };
// Move idx_prev one step back.
if (idx_prev == 0) int idx_prev = 0;
idx_prev = polygon.points.size() - 1; int idx_curr = 0;
else int idx_next = 0;
--idx_prev;
// Move idx_next up until the distance between idx_curr and idx_next is greater than min_arm_length. float distance_to_prev = 0;
if (idx_curr <= idx_next) { float distance_to_next = 0;
while (idx_next < polygon.points.size() && lengths[idx_next] - lengths[idx_curr] < min_arm_length)
++idx_next; //push idx_prev far enough back as initialization
if (idx_next == polygon.points.size()) while (distance_to_prev < min_arm_length) {
idx_next = 0; idx_prev = make_idx_circular(idx_prev - 1);
distance_to_prev += lengths[idx_prev];
}
for (size_t _i = 0; _i < polygon.size(); ++_i) {
// pull idx_prev to current as much as possible, while respecting the min_arm_length
while (distance_to_prev - lengths[idx_prev] > min_arm_length) {
distance_to_prev -= lengths[idx_prev];
idx_prev = make_idx_circular(idx_prev + 1);
} }
while (idx_next < idx_curr && lengths.back() - lengths[idx_curr] + lengths[idx_next] < min_arm_length)
++idx_next; //push idx_next forward as far as needed
while (distance_to_next < min_arm_length) {
distance_to_next += lengths[idx_next];
idx_next = make_idx_circular(idx_next + 1);
}
// Calculate angle between idx_prev, idx_curr, idx_next. // Calculate angle between idx_prev, idx_curr, idx_next.
const Point &p0 = polygon.points[idx_prev]; const Point &p0 = polygon.points[idx_prev];
const Point &p1 = polygon.points[idx_curr]; const Point &p1 = polygon.points[idx_curr];
@ -218,10 +217,16 @@ std::vector<float> calculate_polygon_angles_at_vertices(const Polygon &polygon,
int64_t dot = int64_t(v1(0)) * int64_t(v2(0)) + int64_t(v1(1)) * int64_t(v2(1)); int64_t dot = int64_t(v1(0)) * int64_t(v2(0)) + int64_t(v1(1)) * int64_t(v2(1));
int64_t cross = int64_t(v1(0)) * int64_t(v2(1)) - int64_t(v1(1)) * int64_t(v2(0)); int64_t cross = int64_t(v1(0)) * int64_t(v2(1)) - int64_t(v1(1)) * int64_t(v2(0));
float angle = float(atan2(float(cross), float(dot))); float angle = float(atan2(float(cross), float(dot)));
angles[idx_curr] = angle; result[idx_curr] = angle;
// increase idx_curr by one
float curr_distance = lengths[idx_curr];
idx_curr++;
distance_to_prev += curr_distance;
distance_to_next -= curr_distance;
} }
return angles; return result;
} }
struct GlobalModelInfo { struct GlobalModelInfo {
@ -348,12 +353,21 @@ Polygons extract_perimeter_polygons(const Layer *layer) {
void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std::vector<SeamCandidate> &result_vec, void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std::vector<SeamCandidate> &result_vec,
const GlobalModelInfo &global_model_info) { const GlobalModelInfo &global_model_info) {
if (orig_polygon.size() == 0) {
return;
}
Polygon polygon = orig_polygon; Polygon polygon = orig_polygon;
bool was_clockwise = polygon.make_counter_clockwise(); bool was_clockwise = polygon.make_counter_clockwise();
std::vector<float> lengths = polygon.parameter_by_length(); std::vector<float> lengths { };
std::vector<float> angles = calculate_polygon_angles_at_vertices(polygon, lengths, for (size_t point_idx = 0; point_idx < polygon.size() - 1; ++point_idx) {
SeamPlacer::polygon_angles_arm_distance); lengths.push_back(std::max((unscale(polygon[point_idx]) - unscale(polygon[point_idx + 1])).norm(), 0.01));
}
lengths.push_back(std::max((unscale(polygon[0]) - unscale(polygon[polygon.size() - 1])).norm(), 0.01));
std::vector<float> local_angles = calculate_polygon_angles_at_vertices(polygon, lengths,
SeamPlacer::polygon_local_angles_arm_distance);
std::shared_ptr<Perimeter> perimeter = std::make_shared<Perimeter>(); std::shared_ptr<Perimeter> perimeter = std::make_shared<Perimeter>();
perimeter->start_index = result_vec.size(); perimeter->start_index = result_vec.size();
@ -372,9 +386,9 @@ void process_perimeter_polygon(const Polygon &orig_polygon, float z_coord, std::
type = EnforcedBlockedSeamPoint::Blocked; type = EnforcedBlockedSeamPoint::Blocked;
} }
float ccw_angle = was_clockwise ? -angles[index] : angles[index]; float local_ccw_angle = was_clockwise ? -local_angles[index] : local_angles[index];
result_vec.emplace_back(unscaled_position, perimeter, ccw_angle, type); result_vec.emplace_back(unscaled_position, perimeter, local_ccw_angle, type);
} }
} }
@ -398,6 +412,7 @@ std::pair<size_t, size_t> find_previous_and_next_perimeter_point(const std::vect
} }
//NOTE: only rough esitmation of overhang distance //NOTE: only rough esitmation of overhang distance
// value represents distance from edge, positive is overhang, negative is inside shape
float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_a, const SeamCandidate &under_b, float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_a, const SeamCandidate &under_b,
const SeamCandidate &under_c) { const SeamCandidate &under_c) {
auto p = Vec2d { point.position.x(), point.position.y() }; auto p = Vec2d { point.position.x(), point.position.y() };
@ -412,24 +427,24 @@ float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_
auto dist_ab = oriented_line_dist(a, b, p); auto dist_ab = oriented_line_dist(a, b, p);
auto dist_bc = oriented_line_dist(b, c, p); auto dist_bc = oriented_line_dist(b, c, p);
if (under_b.ccw_angle > 0 && dist_ab > 0 && dist_bc > 0) { //convex shape, p is inside if (under_b.local_ccw_angle > 0 && dist_ab > 0 && dist_bc > 0) { //convex shape, p is inside
return 0; return -((p - b).norm() + dist_ab + dist_bc) / 3.0;
} }
if (under_b.ccw_angle < 0 && (dist_ab < 0 || dist_bc < 0)) { //concave shape, p is inside if (under_b.local_ccw_angle < 0 && (dist_ab < 0 || dist_bc < 0)) { //concave shape, p is inside
return 0; return -((p - b).norm() + dist_ab + dist_bc) / 3.0;
} }
return (p - b).norm(); return ((p - b).norm() + dist_ab + dist_bc) / 3.0;
} }
template<typename CompareFunc> template<typename Comparator>
void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index, void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index,
const CompareFunc &is_first_better) { const Comparator &comparator) {
size_t end_index = perimeter_points[start_index].perimeter->end_index; size_t end_index = perimeter_points[start_index].perimeter->end_index;
size_t seam_index = start_index; size_t seam_index = start_index;
for (size_t index = start_index + 1; index <= end_index; ++index) { for (size_t index = start_index + 1; index <= end_index; ++index) {
if (is_first_better(perimeter_points[index], perimeter_points[seam_index])) { if (comparator.is_first_better(perimeter_points[index], perimeter_points[seam_index])) {
seam_index = index; seam_index = index;
} }
} }
@ -481,8 +496,21 @@ void gather_global_model_info(GlobalModelInfo &result, const PrintObject *po) {
} }
struct DefaultSeamComparator { struct DefaultSeamComparator {
//is A better? static constexpr float angle_clusters[] { -1.0, 0.4 * PI, 0.6
bool operator()(const SeamCandidate &a, const SeamCandidate &b) const { * PI, 0.7 * PI, 0.8 * PI, 0.9 * PI };
const float get_angle_category(float ccw_angle) const {
float concave_bonus = ccw_angle < 0 ? 0.1 : 0;
float abs_angle = abs(ccw_angle) + concave_bonus;
auto category = std::find_if_not(std::begin(angle_clusters), std::end(angle_clusters),
[&](float category_limit) {
return abs_angle > category_limit;
});
category--;
return *category;
}
bool is_first_better(const SeamCandidate &a, const SeamCandidate &b) 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;
@ -492,35 +520,52 @@ struct DefaultSeamComparator {
} }
//avoid overhangs //avoid overhangs
if (a.overhang > 0.5f && b.overhang < a.overhang) { if (a.overhang > 0.3f && b.overhang < a.overhang) {
return false; return false;
} }
auto angle_score = [](float ccw_angle) { { //local angles
if (ccw_angle > 0) { float a_local_category = get_angle_category(a.local_ccw_angle);
float normalized = (ccw_angle / float(PI)) * 0.9f; float b_local_category = get_angle_category(b.local_ccw_angle);
return normalized;
} else { if (a_local_category > b_local_category) {
float normalized = (-ccw_angle / float(PI)) * 1.1f; return true;
return normalized;
} }
}; if (a_local_category < b_local_category) {
float angle_weight = 2.0f; return false;
}
}
auto vis_score = [](float visibility) { return a.visibility < b.visibility;
return (1.0f - visibility / SeamPlacer::expected_hits_per_area); }
};
float vis_weight = 1.2f;
float score_a = angle_score(a.ccw_angle) * angle_weight + bool is_first_not_much_worse(const SeamCandidate &a, const SeamCandidate &b) const {
vis_score(a.visibility) * vis_weight; // Blockers/Enforcers discrimination, top priority
float score_b = angle_score(b.ccw_angle) * angle_weight + if (a.type > b.type) {
vis_score(b.visibility) * vis_weight;
if (score_a > score_b)
return true; return true;
else }
if (b.type > a.type) {
return false; return false;
}
//avoid overhangs
if (a.overhang > 0.3f && b.overhang < a.overhang) {
return false;
}
{ //local angles
float a_local_category = get_angle_category(a.local_ccw_angle) + 0.1 * PI; //give a slight bonus
float b_local_category = get_angle_category(b.local_ccw_angle);
if (a_local_category > b_local_category) {
return true;
}
if (a_local_category < b_local_category) {
return false;
}
}
return a.visibility < b.visibility * 1.3;
} }
} }
; ;
@ -546,8 +591,8 @@ tbb::parallel_for(tbb::blocked_range<size_t>(0, po->layers().size()),
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());
} }
} }
); );
@ -561,7 +606,8 @@ tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po
[&](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) {
for (auto &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) { for (auto &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) {
perimeter_point.visibility = global_model_info.calculate_point_visibility(perimeter_point.position); perimeter_point.visibility = global_model_info.calculate_point_visibility(
perimeter_point.position);
} }
} }
}); });
@ -574,31 +620,155 @@ tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po
[&](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) {
for (SeamCandidate &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) { for (SeamCandidate &perimeter_point : m_perimeter_points_per_object[po][layer_idx]) {
if (layer_idx > 0) { const auto calculate_layer_overhang = [&](size_t other_layer_idx) {
size_t closest_supporter = find_closest_point( size_t closest_supporter = find_closest_point(
*m_perimeter_points_trees_per_object[po][layer_idx - 1], *m_perimeter_points_trees_per_object[po][other_layer_idx],
perimeter_point.position); perimeter_point.position);
const SeamCandidate &supporter_point = const SeamCandidate &supporter_point =
m_perimeter_points_per_object[po][layer_idx - 1][closest_supporter]; m_perimeter_points_per_object[po][other_layer_idx][closest_supporter];
auto prev_next = find_previous_and_next_perimeter_point(m_perimeter_points_per_object[po][layer_idx-1], closest_supporter); auto prev_next = find_previous_and_next_perimeter_point(m_perimeter_points_per_object[po][other_layer_idx], closest_supporter);
const SeamCandidate &prev_point = const SeamCandidate &prev_point =
m_perimeter_points_per_object[po][layer_idx - 1][prev_next.first]; m_perimeter_points_per_object[po][other_layer_idx][prev_next.first];
const SeamCandidate &next_point = const SeamCandidate &next_point =
m_perimeter_points_per_object[po][layer_idx - 1][prev_next.second]; m_perimeter_points_per_object[po][other_layer_idx][prev_next.second];
perimeter_point.overhang = calculate_overhang(perimeter_point, prev_point, return calculate_overhang(perimeter_point, prev_point,
supporter_point, next_point); supporter_point, next_point);
};
if (layer_idx > 0) { //calculate overhang
perimeter_point.overhang = calculate_layer_overhang(layer_idx-1);
}
if (layer_idx < m_perimeter_points_per_object[po].size() - 1) { //calculate higher_layer_overhang
perimeter_point.higher_layer_overhang = calculate_layer_overhang(layer_idx+1);
} }
} }
} }
}); });
} }
void SeamPlacer::distribute_seam_positions_for_alignment(const PrintObject *po) { // 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_string(const PrintObject *po, const Vec3f &last_point_pos,
size_t layer_idx, const Comparator &comparator,
std::vector<std::pair<size_t, size_t>> &seam_strings,
std::vector<std::pair<size_t, size_t>> &potential_string_seams) {
using namespace SeamPlacerImpl; using namespace SeamPlacerImpl;
Vec3f projected_position { last_point_pos.x(), last_point_pos.y(), float(
po->get_layer(layer_idx)->slice_z) };
//find closest point in next layer
size_t closest_point_index = find_closest_point(
*m_perimeter_points_trees_per_object[po][layer_idx], projected_position);
SeamCandidate &closest_point = m_perimeter_points_per_object[po][layer_idx][closest_point_index];
if (closest_point.perimeter->aligned) { //already aligned, skip
return false;
}
//from the closest point, deduce index of seam in the next layer
SeamCandidate &next_layer_seam =
m_perimeter_points_per_object[po][layer_idx][closest_point.perimeter->seam_index];
if ((next_layer_seam.position - projected_position).norm()
< SeamPlacer::seam_align_tolerable_dist) { //seam point is within limits, put in the close_by_points list
seam_strings.emplace_back(layer_idx, closest_point.perimeter->seam_index);
return true;
} else if ((closest_point.position - projected_position).norm()
< SeamPlacer::seam_align_tolerable_dist
&& comparator.is_first_not_much_worse(closest_point, next_layer_seam)) {
//seam point is far, but if the close point is not much worse, do not count it as a skip and add it to potential_string_seams
potential_string_seams.emplace_back(layer_idx, closest_point_index);
return true;
} else {
return false;
}
}
//https://towardsdatascience.com/least-square-polynomial-fitting-using-c-eigen-package-c0673728bd01
template<typename Comparator>
void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comparator) {
using namespace SeamPlacerImpl;
for (size_t layer_idx = 0; layer_idx < m_perimeter_points_per_object[po].size(); ++layer_idx) {
std::vector<SeamCandidate> &layer_perimeter_points =
m_perimeter_points_per_object[po][layer_idx];
size_t current_point_index = 0;
while (current_point_index < layer_perimeter_points.size()) {
if (layer_perimeter_points[current_point_index].perimeter->aligned) {
//skip
} else {
int skips = SeamPlacer::seam_align_tolerable_skips;
int next_layer = layer_idx - 1;
Vec3f last_point_pos = layer_perimeter_points[current_point_index].position;
std::vector<std::pair<size_t, size_t>> seam_string;
std::vector<std::pair<size_t, size_t>> potential_string_seams;
//find close by points and outliers; there is a budget of skips allowed
// search from bottom up in z dir. Heuristics which avoids smooth top surfaces (like head) where the seam is not well defined
while (skips >= 0 && next_layer >= 0) {
if (find_next_seam_in_string(po, last_point_pos, next_layer, comparator, seam_string,
potential_string_seams)) {
last_point_pos =
m_perimeter_points_per_object[po][seam_string.back().first][seam_string.back().second].position;
} else {
skips--;
}
next_layer--;
}
if (seam_string.size() > 4) { //string long enough to be worth aligning
//do additional check in back direction
next_layer = layer_idx;
skips = SeamPlacer::seam_align_tolerable_skips;
while (skips >= 0 && next_layer < int(m_perimeter_points_per_object[po].size())) {
if (find_next_seam_in_string(po, last_point_pos, next_layer, DefaultSeamComparator { },
seam_string,
potential_string_seams)) {
last_point_pos =
m_perimeter_points_per_object[po][seam_string.back().first][seam_string.back().second].position;
} else {
skips--;
}
next_layer++;
}
// all string seams and potential string seams gathered, now do the alignment
seam_string.insert(seam_string.end(), potential_string_seams.begin(), potential_string_seams.end());
std::sort(seam_string.begin(), seam_string.end(),
[](const std::pair<size_t, size_t> &left, const std::pair<size_t, size_t> &right) {
return left.first < right.first;
});
//https://en.wikipedia.org/wiki/Exponential_smoothing
//inititalization
float smoothing_factor = 0.5;
std::pair<size_t, size_t> init = seam_string[0];
Vec2f prev_pos_xy = m_perimeter_points_per_object[po][init.first][init.second].position.head<2>();
for (const auto &pair : seam_string) {
Vec3f current_pos = m_perimeter_points_per_object[po][pair.first][pair.second].position;
float current_height = current_pos.z();
Vec2f current_pos_xy = current_pos.head<2>();
current_pos_xy = smoothing_factor * prev_pos_xy + (1.0 - smoothing_factor) * current_pos_xy;
Perimeter *perimeter =
m_perimeter_points_per_object[po][pair.first][pair.second].perimeter.get();
perimeter->final_seam_position =
Vec3f { current_pos_xy.x(), current_pos_xy.y(), current_height };
perimeter->aligned = true;
prev_pos_xy = current_pos_xy;
}
} // else string is not long enough, so dont do anything
}
current_point_index = layer_perimeter_points[current_point_index].perimeter->end_index + 1;
}
}
} }
void SeamPlacer::init(const Print &print) { void SeamPlacer::init(const Print &print) {
@ -630,31 +800,29 @@ void SeamPlacer::init(const Print &print) {
<< "SeamPlacer: calculate_overhangs : end"; << "SeamPlacer: calculate_overhangs : end";
BOOST_LOG_TRIVIAL(debug) BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: distribute_seam_positions_for_alignment, pick_seams : start"; << "SeamPlacer: pick_seam_point : start";
//pick seam point
for (size_t iteration = 0; iteration < seam_align_iterations; ++iteration) { tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
if (iteration > 0) { //skip this in first iteration, no seam has been picked yet; nothing to distribute for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
distribute_seam_positions_for_alignment(po); std::vector<SeamCandidate> &layer_perimeter_points =
} m_perimeter_points_per_object[po][layer_idx];
size_t current = 0;
//pick seam point while (current < layer_perimeter_points.size()) {
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()), pick_seam_point(layer_perimeter_points, current, DefaultSeamComparator { });
[&](tbb::blocked_range<size_t> r) { current = layer_perimeter_points[current].perimeter->end_index + 1;
for (size_t layer_idx = r.begin(); layer_idx < r.end(); ++layer_idx) {
std::vector<SeamCandidate> &layer_perimeter_points =
m_perimeter_points_per_object[po][layer_idx];
size_t current = 0;
while (current < layer_perimeter_points.size()) {
//NOTE: pick seam point function also resets the m_nearby_seam_points count on all passed points
pick_seam_point(layer_perimeter_points, current, DefaultSeamComparator { });
current = layer_perimeter_points[current].perimeter->end_index + 1;
}
} }
}); }
} });
BOOST_LOG_TRIVIAL(debug) BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: distribute_seam_positions_for_alignment, pick_seams : end"; << "SeamPlacer: pick_seam_point : end";
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: align_seam_points : start";
align_seam_points(po, DefaultSeamComparator { });
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: align_seam_points : end";
} }
} }
@ -683,36 +851,4 @@ void SeamPlacer::place_seam(const Layer *layer, ExtrusionLoop &loop, bool extern
loop.split_at(seam_point, true); loop.split_at(seam_point, true);
} }
// Disabled debug code, can be used to export debug data into obj files (e.g. point cloud of visibility hits)
#if 0
#include <boost/nowide/cstdio.hpp>
Slic3r::CNumericLocalesSetter locales_setter;
FILE *fp = boost::nowide::fopen("perimeters.obj", "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "Couldn't open " << "perimeters.obj" << " for writing";
}
for (size_t i = 0; i < perimeter_points.size(); ++i)
fprintf(fp, "v %f %f %f %f\n", perimeter_points[i].position[0], perimeter_points[i].position[1],
perimeter_points[i].position[2], perimeter_points[i].visibility);
fclose(fp);
#endif
#if 0
its_write_obj(triangles, "triangles.obj");
Slic3r::CNumericLocalesSetter locales_setter;
FILE *fp = boost::nowide::fopen("hits.obj", "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "Couldn't open " << "hits.obj" << " for writing";
}
for (size_t i = 0; i < hit_points.size(); ++i)
fprintf(fp, "v %f %f %f \n", hit_points[i].position[0], hit_points[i].position[1],
hit_points[i].position[2]);
fclose(fp);
#endif
} // namespace Slic3r } // namespace Slic3r

View File

@ -38,18 +38,23 @@ struct Perimeter {
size_t start_index; size_t start_index;
size_t end_index; size_t end_index;
size_t seam_index; size_t seam_index;
bool aligned = false;
Vec3f final_seam_position;
}; };
struct SeamCandidate { struct SeamCandidate {
SeamCandidate(const Vec3f &pos, std::shared_ptr<Perimeter> perimeter, float ccw_angle, SeamCandidate(const Vec3f &pos, std::shared_ptr<Perimeter> perimeter, float local_ccw_angle,
EnforcedBlockedSeamPoint type) : EnforcedBlockedSeamPoint type) :
position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), ccw_angle(ccw_angle), type(type) { position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), higher_layer_overhang(0.0f), local_ccw_angle(
local_ccw_angle), type(type) {
} }
const Vec3f position; const Vec3f position;
const std::shared_ptr<Perimeter> perimeter; const std::shared_ptr<Perimeter> perimeter;
float visibility; float visibility;
float overhang; float overhang;
float ccw_angle; float higher_layer_overhang; // represents how much is the position covered by the upper layer, useful for local visibility
float local_ccw_angle;
EnforcedBlockedSeamPoint type; EnforcedBlockedSeamPoint type;
}; };
@ -88,12 +93,12 @@ public:
static constexpr float cosine_hemisphere_sampling_power = 6.0f; static constexpr float cosine_hemisphere_sampling_power = 6.0f;
static constexpr float polygon_angles_arm_distance = 0.6f; static constexpr float polygon_local_angles_arm_distance = 0.6f;
static constexpr float enforcer_blocker_sqr_distance_tolerance = 0.4f; static constexpr float enforcer_blocker_sqr_distance_tolerance = 0.2f;
static constexpr size_t seam_align_iterations = 1;
static constexpr size_t seam_align_layer_dist = 30; static constexpr float seam_align_tolerable_dist = 1.5f;
static constexpr float seam_align_tolerable_dist = 0.3f; static constexpr size_t seam_align_tolerable_skips = 4;
//perimeter points per object per layer idx, and their corresponding KD trees //perimeter points per object per layer idx, and their corresponding KD trees
std::unordered_map<const PrintObject*, std::vector<std::vector<SeamPlacerImpl::SeamCandidate>>> m_perimeter_points_per_object; std::unordered_map<const PrintObject*, std::vector<std::vector<SeamPlacerImpl::SeamCandidate>>> m_perimeter_points_per_object;
std::unordered_map<const PrintObject*, std::vector<std::unique_ptr<SeamCandidatesTree>>> m_perimeter_points_trees_per_object; std::unordered_map<const PrintObject*, std::vector<std::unique_ptr<SeamCandidatesTree>>> m_perimeter_points_trees_per_object;
@ -107,7 +112,13 @@ private:
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);
void distribute_seam_positions_for_alignment(const PrintObject *po); template<typename Comparator>
void align_seam_points(const PrintObject *po, const Comparator &comparator);
template<typename Comparator>
bool find_next_seam_in_string(const PrintObject *po, const Vec3f &last_point_pos,
size_t layer_idx, const Comparator &comparator,
std::vector<std::pair<size_t, size_t>> &seam_strings,
std::vector<std::pair<size_t, size_t>> &outliers);
}; };
} // namespace Slic3r } // namespace Slic3r