debug export svg info,

fixing problem with weird seam placement caused by disconnected scoring function
This commit is contained in:
PavelMikus 2022-03-04 14:13:17 +01:00
parent 962282c9ef
commit a92d5038bd
2 changed files with 128 additions and 70 deletions

View file

@ -26,12 +26,25 @@
#ifdef DEBUG_FILES
#include "Subdivide.hpp"
#include <boost/nowide/cstdio.hpp>
#include <SVG.hpp>
#endif
namespace Slic3r {
namespace SeamPlacerImpl {
Vec3f value_to_rgbf(float minimum, float maximum, float value) {
float ratio = 2.0f * (value - minimum) / (maximum - minimum);
float b = std::max(0.0f, (1.0f - ratio));
float r = std::max(0.0f, (ratio - 1.0f));
float g = 1.0f - b - r;
return Vec3f { r, g, b };
}
Vec3i value_rgbi(float minimum, float maximum, float value) {
return (value_to_rgbf(minimum, maximum, value) * 255).cast<int>();
}
//https://towardsdatascience.com/least-square-polynomial-fitting-using-c-eigen-package-c0673728bd01
// interpolates points in z (treats z coordinates as time) and returns coefficients for axis x and y
std::vector<Vec2f> polyfit(const std::vector<Vec3f> &points, const std::vector<float> &weights, size_t order) {
@ -350,7 +363,7 @@ struct GlobalModelInfo {
#ifdef DEBUG_FILES
void debug_export(const indexed_triangle_set &obj_mesh, const char *file_name) const {
indexed_triangle_set divided_mesh = subdivide(obj_mesh, SeamPlacer::considered_area_radius);
indexed_triangle_set divided_mesh = obj_mesh; // subdivide(obj_mesh, SeamPlacer::considered_area_radius);
Slic3r::CNumericLocalesSetter locales_setter;
{
FILE *fp = boost::nowide::fopen(file_name, "w");
@ -402,7 +415,6 @@ struct GlobalModelInfo {
}
}
#endif
}
;
@ -669,8 +681,10 @@ struct SeamComparator {
setup(setup) {
}
//TODO "bump function": e^[1 / ( (x-0.3)^2 +1 )] -1 << =^(((1)/((x-0.3)^(2)+1)))-1
float compute_angle_penalty(float ccw_angle) const {
if (ccw_angle >= 0) {
if (ccw_angle >= -0.4) {
return PI * PI - ccw_angle * ccw_angle;
} else {
return (PI * PI - ccw_angle * ccw_angle) * 0.7f;
@ -727,93 +741,133 @@ struct SeamComparator {
float get_weight(const SeamCandidate &a) const {
if (setup == SeamPosition::spRear) {
return a.position.y() + int(a.type) * 10000.0f;
return a.position.y();
}
//return negative, beacuse we want to minimize this value (sort of antiweight). normalization in alignment fixes that with respect to other points
return int(a.type) * 10000.0f
- (a.visibility + SeamPlacer::expected_hits_per_area) * compute_angle_penalty(a.local_ccw_angle);
//return negative, beacuse we want to minimize the absolute of this value (sort of antiweight). normalization in alignment fixes that with respect to other points
return -(a.visibility + SeamPlacer::expected_hits_per_area) * compute_angle_penalty(a.local_ccw_angle);
}
}
;
#ifdef DEBUG_FILES
void debug_export_points(const std::vector<std::vector<SeamPlacerImpl::SeamCandidate>> &object_perimter_points,
const BoundingBox &bounding_box, std::string object_name, const SeamComparator &comparator) {
for (size_t layer_idx = 0; layer_idx < object_perimter_points.size(); ++layer_idx) {
SVG angles_svg { object_name + "_angles_" + std::to_string(layer_idx) + ".svg", bounding_box };
float min_vis = 0;
float max_vis = min_vis;
float min_weight = std::numeric_limits<float>::min();
float max_weight = min_weight;
for (const SeamCandidate &point : object_perimter_points[layer_idx]) {
Vec3i color = value_rgbi(-PI, PI, point.local_ccw_angle);
std::string fill = "rgb(" + std::to_string(color.x()) + "," + std::to_string(color.y()) + ","
+ std::to_string(color.z()) + ")";
angles_svg.draw(scaled(Vec2f(point.position.head<2>())), fill);
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));
}
SVG visibility_svg { object_name + "_visibility_" + std::to_string(layer_idx) + ".svg", bounding_box };
SVG weight_svg { object_name + "_weight_" + std::to_string(layer_idx) + ".svg", bounding_box };
for (const SeamCandidate &point : object_perimter_points[layer_idx]) {
Vec3i color = value_rgbi(min_vis, max_vis, point.visibility);
std::string visibility_fill = "rgb(" + std::to_string(color.x()) + "," + std::to_string(color.y()) + ","
+ 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));
std::string weight_fill = "rgb(" + std::to_string(weight_color.x()) + "," + std::to_string(weight_color.y()) + ","
+ std::to_string(weight_color.z()) + ")";
weight_svg.draw(scaled(Vec2f(point.position.head<2>())), weight_fill);
}
}
}
#endif
} // namespace SeamPlacerImpl
// Parallel process and extract each perimeter polygon of the given print object.
// Gather SeamCandidates of each layer into vector and build KDtree over them
// Store results in the SeamPlacer varaibles m_perimeter_points_per_object and m_perimeter_points_trees_per_object
void SeamPlacer::gather_seam_candidates(const PrintObject *po,
const SeamPlacerImpl::GlobalModelInfo &global_model_info) {
using namespace SeamPlacerImpl;
const SeamPlacerImpl::GlobalModelInfo &global_model_info) {
using namespace SeamPlacerImpl;
m_perimeter_points_per_object.emplace(po, po->layer_count());
m_perimeter_points_trees_per_object.emplace(po, po->layer_count());
m_perimeter_points_per_object.emplace(po, po->layer_count());
m_perimeter_points_trees_per_object.emplace(po, po->layer_count());
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];
const Layer *layer = po->get_layer(layer_idx);
auto unscaled_z = layer->slice_z;
Polygons polygons = extract_perimeter_polygons(layer);
for (const auto &poly : polygons) {
process_perimeter_polygon(poly, unscaled_z, layer_candidates,
global_model_info);
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];
const Layer *layer = po->get_layer(layer_idx);
auto unscaled_z = layer->slice_z;
Polygons polygons = extract_perimeter_polygons(layer);
for (const auto &poly : polygons) {
process_perimeter_polygon(poly, unscaled_z, layer_candidates,
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());
}
auto functor = SeamCandidateCoordinateFunctor { &layer_candidates };
m_perimeter_points_trees_per_object[po][layer_idx] = std::make_unique<SeamCandidatesTree>(
functor, layer_candidates.size());
}
}
);
);
}
void SeamPlacer::calculate_candidates_visibility(const PrintObject *po,
const SeamPlacerImpl::GlobalModelInfo &global_model_info) {
using namespace SeamPlacerImpl;
const SeamPlacerImpl::GlobalModelInfo &global_model_info) {
using namespace SeamPlacerImpl;
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
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]) {
perimeter_point.visibility = global_model_info.calculate_point_visibility(
perimeter_point.position);
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
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]) {
perimeter_point.visibility = global_model_info.calculate_point_visibility(
perimeter_point.position);
}
}
}
});
});
}
void SeamPlacer::calculate_overhangs(const PrintObject *po) {
using namespace SeamPlacerImpl;
using namespace SeamPlacerImpl;
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
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]) {
const auto calculate_layer_overhang = [&](size_t other_layer_idx) {
size_t closest_supporter = find_closest_point(
*m_perimeter_points_trees_per_object[po][other_layer_idx],
perimeter_point.position);
const SeamCandidate &supporter_point =
m_perimeter_points_per_object[po][other_layer_idx][closest_supporter];
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
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]) {
const auto calculate_layer_overhang = [&](size_t other_layer_idx) {
size_t closest_supporter = find_closest_point(
*m_perimeter_points_trees_per_object[po][other_layer_idx],
perimeter_point.position);
const SeamCandidate &supporter_point =
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][other_layer_idx], closest_supporter);
const SeamCandidate &prev_point =
m_perimeter_points_per_object[po][other_layer_idx][prev_next.first];
const SeamCandidate &next_point =
m_perimeter_points_per_object[po][other_layer_idx][prev_next.second];
auto prev_next = find_previous_and_next_perimeter_point(m_perimeter_points_per_object[po][other_layer_idx], closest_supporter);
const SeamCandidate &prev_point =
m_perimeter_points_per_object[po][other_layer_idx][prev_next.first];
const SeamCandidate &next_point =
m_perimeter_points_per_object[po][other_layer_idx][prev_next.second];
return calculate_overhang(perimeter_point, prev_point,
supporter_point, next_point);
};
return calculate_overhang(perimeter_point, prev_point,
supporter_point, next_point);
};
if (layer_idx > 0) { //calculate overhang
perimeter_point.overhang = calculate_layer_overhang(layer_idx-1);
if (layer_idx > 0) { //calculate overhang
perimeter_point.overhang = calculate_layer_overhang(layer_idx-1);
}
}
}
}
});
}
});
}
// Estimates, if there is good seam point in the layer_idx which is close to last_point_pos
// uses comparator.is_first_not_much_worse method to compare current seam with the closest point
@ -830,7 +884,8 @@ bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po,
std::vector<std::pair<size_t, size_t>> &potential_string_seams) {
using namespace SeamPlacerImpl;
const SeamCandidate& last_point = m_perimeter_points_per_object[po][last_point_indexes.first][last_point_indexes.second];
const SeamCandidate &last_point =
m_perimeter_points_per_object[po][last_point_indexes.first][last_point_indexes.second];
Vec3f projected_position { last_point.position.x(), last_point.position.y(), float(
po->get_layer(layer_idx)->slice_z) };
@ -848,21 +903,22 @@ bool SeamPlacer::find_next_seam_in_layer(const PrintObject *po,
SeamCandidate &next_layer_seam =
m_perimeter_points_per_object[po][layer_idx][closest_point.perimeter->seam_index];
auto are_similar = [&](const SeamCandidate& a, const SeamCandidate& b) {
return comparator.is_first_not_much_worse(a,b) && comparator.is_first_not_much_worse(b,a);
auto are_similar = [&](const SeamCandidate &a, const SeamCandidate &b) {
return comparator.is_first_not_much_worse(a, b) && comparator.is_first_not_much_worse(b, a);
};
if ((next_layer_seam.position - projected_position).norm()
< SeamPlacer::seam_align_tolerable_dist && are_similar(last_point, next_layer_seam)) { //seam point is within limits, put in the close_by_points list
seam_string.emplace_back(layer_idx, closest_point.perimeter->seam_index);
last_point_indexes = std::pair<size_t,size_t>{layer_idx, closest_point.perimeter->seam_index};
last_point_indexes = std::pair<size_t, size_t> { layer_idx, closest_point.perimeter->seam_index };
return true;
} else if ((closest_point.position - projected_position).norm()
< SeamPlacer::seam_align_tolerable_dist
&& comparator.is_first_not_much_worse(closest_point, next_layer_seam) && are_similar(last_point, closest_point)) {
&& comparator.is_first_not_much_worse(closest_point, next_layer_seam)
&& are_similar(last_point, closest_point)) {
//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);
last_point_indexes = std::pair<size_t,size_t>{layer_idx, closest_point_index};
last_point_indexes = std::pair<size_t, size_t> { layer_idx, closest_point_index };
return true;
} else {
return false;
@ -932,7 +988,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
//initialize searching for seam string - cluster of nearby seams on previous and next layers
int skips = SeamPlacer::seam_align_tolerable_skips / 2;
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>> potential_string_seams;
@ -952,7 +1008,7 @@ void SeamPlacer::align_seam_points(const PrintObject *po, const Comparator &comp
//do additional check in back direction
next_layer = layer_idx - 1;
skips = SeamPlacer::seam_align_tolerable_skips / 2;
last_point_indexes = std::pair<size_t,size_t>(layer_idx, seam_index);
last_point_indexes = std::pair<size_t, size_t>(layer_idx, seam_index);
while (skips >= 0 && next_layer >= 0) {
if (find_next_seam_in_layer(po, last_point_indexes, next_layer, comparator,
seam_string,
@ -1131,6 +1187,8 @@ void SeamPlacer::init(const Print &print) {
BOOST_LOG_TRIVIAL(debug)
<< "SeamPlacer: align_seam_points : end";
}
debug_export_points(m_perimeter_points_per_object[po], po->bounding_box(), std::to_string(po->id().id), comparator);
}
}

View file

@ -101,11 +101,11 @@ public:
// area considered when computing number of rays and then gathering visiblity info from the hits
static constexpr float considered_area_radius = 3.0f;
// quadric error limit of quadric decimation function used on the mesh before raycasting
static constexpr float raycasting_decimation_target_error = 0.1f;
static constexpr float raycasting_decimation_target_error = 2.0f;
// cosine sampling power represents how prefered are forward directions when raycasting from given spot
// in this case, forward direction means towards the center of the mesh
static constexpr float cosine_hemisphere_sampling_power = 8.0f;
static constexpr float cosine_hemisphere_sampling_power = 5.0f;
// arm length used during angles computation
static constexpr float polygon_local_angles_arm_distance = 1.0f;