implemented overhang calculation and alignemnt iterations for seams
now only external perimeters are considered which reduced time complexity
This commit is contained in:
parent
53e9bb3ebf
commit
e8f740dabb
@ -100,17 +100,17 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
|
||||
};
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: generate random samples: start";
|
||||
<< "SeamPlacer: generate random samples: start";
|
||||
std::vector<Vec2f> global_dir_random_samples(ray_count);
|
||||
generate(begin(global_dir_random_samples), end(global_dir_random_samples), gen);
|
||||
std::vector<Vec2f> local_dir_random_samples(ray_count);
|
||||
generate(begin(local_dir_random_samples), end(local_dir_random_samples), gen);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: generate random samples: end";
|
||||
<< "SeamPlacer: generate random samples: end";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: raycast visibility for " << ray_count << " rays: start";
|
||||
<< "SeamPlacer: raycast visibility for " << ray_count << " rays: start";
|
||||
// raycast visibility
|
||||
std::vector<HitInfo> hit_points = tbb::parallel_reduce(tbb::blocked_range<size_t>(0, ray_count),
|
||||
std::vector<HitInfo> { },
|
||||
@ -151,24 +151,7 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
|
||||
);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: raycast visibility for " << ray_count << " rays: end";
|
||||
|
||||
//TODO disable, only debug code
|
||||
#ifdef DEBUG_FILES
|
||||
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].m_position[0], hit_points[i].m_position[1],
|
||||
hit_points[i].m_position[2]);
|
||||
fclose(fp);
|
||||
#endif
|
||||
<< "SeamPlacer: raycast visibility for " << ray_count << " rays: end";
|
||||
|
||||
return hit_points;
|
||||
}
|
||||
@ -230,18 +213,59 @@ std::vector<float> calculate_polygon_angles_at_vertices(const Polygon &polygon,
|
||||
return angles;
|
||||
}
|
||||
|
||||
template<typename EnforcerDistance, typename BlockerDistance>
|
||||
void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::vector<SeamCandidate> &result_vec,
|
||||
const EnforcerDistance &enforcer_distance_check, const BlockerDistance &blocker_distance_check) {
|
||||
struct GlobalModelInfo {
|
||||
std::vector<HitInfo> geometry_raycast_hits;
|
||||
KDTreeIndirect<3, coordf_t, HitInfoCoordinateFunctor> raycast_hits_tree;
|
||||
indexed_triangle_set enforcers;
|
||||
indexed_triangle_set blockers;
|
||||
AABBTreeIndirect::Tree<3, float> enforcers_tree;
|
||||
AABBTreeIndirect::Tree<3, float> blockers_tree;
|
||||
|
||||
GlobalModelInfo() :
|
||||
raycast_hits_tree(HitInfoCoordinateFunctor { &geometry_raycast_hits }) {
|
||||
}
|
||||
|
||||
double enforcer_distance_check(const Vec3d &position) const {
|
||||
size_t hit_idx_out;
|
||||
Vec3d closest_vec3d;
|
||||
return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(enforcers.vertices, enforcers.indices,
|
||||
enforcers_tree, position, hit_idx_out, closest_vec3d);
|
||||
}
|
||||
|
||||
double blocker_distance_check(const Vec3d &position) const {
|
||||
size_t hit_idx_out;
|
||||
Vec3d closest_vec3d;
|
||||
return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(blockers.vertices, blockers.indices,
|
||||
blockers_tree, position, hit_idx_out, closest_vec3d);
|
||||
}
|
||||
|
||||
double calculate_point_visibility(const Vec3d &position, double max_distance) const {
|
||||
auto nearby_points = find_nearby_points(raycast_hits_tree, position, max_distance);
|
||||
double visibility = 0;
|
||||
for (const auto &hit_point_index : nearby_points) {
|
||||
double distance =
|
||||
(position - geometry_raycast_hits[hit_point_index].m_position).norm();
|
||||
visibility += max_distance - distance; // The further away from the perimeter point,
|
||||
// the less representative ray hit is
|
||||
}
|
||||
return visibility;
|
||||
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
void process_perimeter_polygon(const Polygon &orig_polygon, coordf_t z_coord, std::vector<SeamCandidate> &result_vec,
|
||||
const GlobalModelInfo &global_model_info) {
|
||||
Polygon polygon = orig_polygon;
|
||||
polygon.make_counter_clockwise();
|
||||
std::vector<float> lengths = polygon.parameter_by_length();
|
||||
std::vector<float> angles = calculate_polygon_angles_at_vertices(polygon, lengths,
|
||||
SeamPlacer::polygon_angles_arm_distance);
|
||||
bool is_ccw = polygon.is_counter_clockwise();
|
||||
|
||||
Vec3d last_enforcer_checked_point { 0, 0, -1 };
|
||||
double enforcer_dist_sqr = enforcer_distance_check(last_enforcer_checked_point);
|
||||
double enforcer_dist_sqr = global_model_info.enforcer_distance_check(last_enforcer_checked_point);
|
||||
Vec3d last_blocker_checked_point { 0, 0, -1 };
|
||||
double blocker_dist_sqr = blocker_distance_check(last_blocker_checked_point);
|
||||
double blocker_dist_sqr = global_model_info.blocker_distance_check(last_blocker_checked_point);
|
||||
|
||||
for (size_t index = 0; index < polygon.size(); ++index) {
|
||||
Vec2d unscaled_p = unscale(polygon[index]);
|
||||
@ -249,9 +273,6 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve
|
||||
EnforcedBlockedSeamPoint type = EnforcedBlockedSeamPoint::NONE;
|
||||
|
||||
float ccw_angle = angles[index];
|
||||
if (!is_ccw) {
|
||||
ccw_angle = -ccw_angle;
|
||||
}
|
||||
|
||||
if (enforcer_dist_sqr >= 0) { // if enforcer dist < 0, it means there are no enforcers, skip
|
||||
//if there is enforcer, any other enforcer cannot be in a sphere defined by last check point and enforcer distance
|
||||
@ -262,7 +283,7 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve
|
||||
(last_enforcer_checked_point - unscaled_position).squaredNorm()
|
||||
>= enforcer_dist_sqr - 2 * SeamPlacer::enforcer_blocker_sqr_distance_tolerance) {
|
||||
//do check
|
||||
enforcer_dist_sqr = enforcer_distance_check(unscaled_position);
|
||||
enforcer_dist_sqr = global_model_info.enforcer_distance_check(unscaled_position);
|
||||
last_enforcer_checked_point = unscaled_position;
|
||||
if (enforcer_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance) {
|
||||
type = EnforcedBlockedSeamPoint::ENFORCED;
|
||||
@ -275,7 +296,7 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve
|
||||
||
|
||||
(last_blocker_checked_point - unscaled_position).squaredNorm()
|
||||
>= blocker_dist_sqr - 2 * SeamPlacer::enforcer_blocker_sqr_distance_tolerance) {
|
||||
blocker_dist_sqr = blocker_distance_check(unscaled_position);
|
||||
blocker_dist_sqr = global_model_info.blocker_distance_check(unscaled_position);
|
||||
last_blocker_checked_point = unscaled_position;
|
||||
if (blocker_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance) {
|
||||
type = EnforcedBlockedSeamPoint::BLOCKED;
|
||||
@ -287,36 +308,78 @@ void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::ve
|
||||
}
|
||||
}
|
||||
|
||||
void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index, size_t end_index) {
|
||||
auto min_visibility = perimeter_points[start_index].m_visibility;
|
||||
auto type = perimeter_points[start_index].m_type;
|
||||
std::pair<size_t, size_t> find_previous_and_next_perimeter_point(const std::vector<SeamCandidate> &perimeter_points,
|
||||
size_t index) {
|
||||
const SeamCandidate ¤t = perimeter_points[index];
|
||||
|
||||
size_t prev = index > 0 ? index - 1 : index;
|
||||
size_t next = index + 1 < perimeter_points.size() ? index + 1 : index;
|
||||
|
||||
//NOTE: dont forget that m_polygon_index_reverse are reversed indexes, so 0 is last point
|
||||
if (current.m_polygon_index_reverse == 0) {
|
||||
// next is at the start of loop
|
||||
//find start
|
||||
size_t start = index;
|
||||
while (start > 0 && perimeter_points[start - 1].m_polygon_index_reverse != 0) {
|
||||
start--;
|
||||
}
|
||||
next = start;
|
||||
}
|
||||
|
||||
if (index > 1 && perimeter_points[index - 1].m_polygon_index_reverse == 0) {
|
||||
//prev is at the end of loop
|
||||
prev = index + perimeter_points[index].m_polygon_index_reverse;
|
||||
}
|
||||
|
||||
return {prev,next};
|
||||
}
|
||||
|
||||
float calculate_overhang(const SeamCandidate &point, const SeamCandidate &under_a, const SeamCandidate &under_b,
|
||||
const SeamCandidate &under_c) {
|
||||
auto p = Vec2d { point.m_position.x(), point.m_position.y() };
|
||||
auto a = Vec2d { under_a.m_position.x(), under_a.m_position.y() };
|
||||
auto b = Vec2d { under_b.m_position.x(), under_b.m_position.y() };
|
||||
auto c = Vec2d { under_c.m_position.x(), under_c.m_position.y() };
|
||||
|
||||
auto oriented_line_dist = [](const Vec2d a, const Vec2d b, const Vec2d p) {
|
||||
return -((b.x() - a.x()) * (a.y() - p.y()) - (a.x() - p.x()) * (b.y() - a.y())) / (a - b).norm();
|
||||
};
|
||||
|
||||
auto dist_ab = oriented_line_dist(a, b, p);
|
||||
auto dist_bc = oriented_line_dist(b, c, p);
|
||||
|
||||
if (under_b.m_ccw_angle > 0 && dist_ab > 0 && dist_bc > 0) { //convex shape, p is inside
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (under_b.m_ccw_angle < 0 && (dist_ab < 0 || dist_bc < 0)) { //concave shape, p is inside
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Vec2d((p - b).norm(), std::min(abs(dist_ab), abs(dist_bc))).norm();
|
||||
|
||||
}
|
||||
|
||||
template<typename CompareFunc>
|
||||
void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_index, size_t end_index,
|
||||
const CompareFunc &is_first_better) {
|
||||
size_t seam_index = start_index;
|
||||
for (size_t index = start_index + 1; index <= end_index; ++index) {
|
||||
if ((perimeter_points[index].m_visibility < min_visibility && perimeter_points[index].m_type == type)
|
||||
||
|
||||
(perimeter_points[index].m_type > type)) {
|
||||
min_visibility = perimeter_points[index].m_visibility;
|
||||
type = perimeter_points[index].m_type;
|
||||
if (is_first_better(perimeter_points[index], perimeter_points[seam_index])) {
|
||||
seam_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t index = start_index; index <= end_index; ++index) {
|
||||
perimeter_points[index].m_seam_index = seam_index;
|
||||
perimeter_points[index].m_nearby_seam_points.get()->store(0, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace SeamPlacerImpl
|
||||
|
||||
void SeamPlacer::init(const Print &print) {
|
||||
using namespace SeamPlacerImpl;
|
||||
m_perimeter_points_trees_per_object.clear();
|
||||
m_perimeter_points_per_object.clear();
|
||||
|
||||
for (const PrintObject *po : print.objects()) {
|
||||
void gather_global_model_info(GlobalModelInfo &result, const PrintObject *po) {
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: build AABB tree for raycasting: start";
|
||||
// Build AABB tree for raycasting
|
||||
<< "SeamPlacer: build AABB tree for raycasting and gather occlusion info: start";
|
||||
// Build AABB tree for raycasting
|
||||
auto obj_transform = po->trafo_centered();
|
||||
auto triangle_set = po->model_object()->raw_indexed_triangle_set();
|
||||
its_transform(triangle_set, obj_transform);
|
||||
@ -324,50 +387,80 @@ void SeamPlacer::init(const Print &print) {
|
||||
auto raycasting_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(triangle_set.vertices,
|
||||
triangle_set.indices);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: build AABB tree for raycasting: end";
|
||||
result.geometry_raycast_hits = raycast_visibility(SeamPlacer::ray_count_per_object, raycasting_tree,
|
||||
triangle_set);
|
||||
result.raycast_hits_tree.build(result.geometry_raycast_hits.size());
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: build AABB trees for raycasting enforcers/blockers: start";
|
||||
<< "SeamPlacer: build AABB tree for raycasting and gather occlusion info: end";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: build AABB trees for raycasting enforcers/blockers: start";
|
||||
|
||||
indexed_triangle_set enforcers { };
|
||||
indexed_triangle_set blockers { };
|
||||
for (const ModelVolume *mv : po->model_object()->volumes) {
|
||||
if (mv->is_model_part()) {
|
||||
its_merge(enforcers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||
its_merge(blockers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::BLOCKER));
|
||||
its_merge(result.enforcers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||
its_merge(result.blockers, mv->seam_facets.get_facets(*mv, EnforcerBlockerType::BLOCKER));
|
||||
}
|
||||
}
|
||||
its_transform(enforcers, obj_transform);
|
||||
its_transform(blockers, obj_transform);
|
||||
its_transform(result.enforcers, obj_transform);
|
||||
its_transform(result.blockers, obj_transform);
|
||||
|
||||
auto enforcers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(enforcers.vertices,
|
||||
enforcers.indices);
|
||||
auto blockers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(blockers.vertices,
|
||||
blockers.indices);
|
||||
|
||||
auto enforcer_distance_check = [&](const Vec3d ¢er) {
|
||||
size_t hit_idx_out;
|
||||
Vec3d closest_vec3d;
|
||||
return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(enforcers.vertices, enforcers.indices,
|
||||
enforcers_tree, center, hit_idx_out, closest_vec3d);
|
||||
};
|
||||
auto blocker_distance_check = [&](const Vec3d ¢er) {
|
||||
size_t hit_idx_out;
|
||||
Vec3d closest_vec3d;
|
||||
return AABBTreeIndirect::squared_distance_to_indexed_triangle_set(blockers.vertices, blockers.indices,
|
||||
blockers_tree, center, hit_idx_out, closest_vec3d);
|
||||
};
|
||||
result.enforcers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(result.enforcers.vertices,
|
||||
result.enforcers.indices);
|
||||
result.blockers_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(result.blockers.vertices,
|
||||
result.blockers.indices);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: build AABB trees for raycasting enforcers/blockers: end";
|
||||
<< "SeamPlacer: build AABB trees for raycasting enforcers/blockers: end";
|
||||
}
|
||||
|
||||
std::vector<HitInfo> hit_points = raycast_visibility(ray_count_per_object, raycasting_tree, triangle_set);
|
||||
HitInfoCoordinateFunctor hit_points_functor { &hit_points };
|
||||
KDTreeIndirect<3, coordf_t, HitInfoCoordinateFunctor> hit_points_tree { hit_points_functor, hit_points.size() };
|
||||
struct DefaultSeamComparator {
|
||||
//is a better?
|
||||
bool operator()(const SeamCandidate &a, const SeamCandidate &b) const {
|
||||
if (a.m_type > b.m_type) {
|
||||
return true;
|
||||
}
|
||||
if (b.m_type > a.m_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if (a.m_overhang > 0.2 && b.m_overhang < a.m_overhang) {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// if (b.m_ccw_angle < -float(0.3 * PI) && a.m_ccw_angle > -float(0.3 * PI)){
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if (*b.m_nearby_seam_points > *a.m_nearby_seam_points) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (b.m_visibility < 1.2*a.m_visibility) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
} // namespace SeamPlacerImpl
|
||||
|
||||
void SeamPlacer::init(const Print &print) {
|
||||
using namespace SeamPlacerImpl;
|
||||
m_perimeter_points_trees_per_object.clear();
|
||||
m_perimeter_points_per_object.clear();
|
||||
|
||||
for (const PrintObject *po : print.objects()) {
|
||||
|
||||
GlobalModelInfo global_model_info { };
|
||||
gather_global_model_info(global_model_info, po);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: gather and build KD trees with seam candidates: start";
|
||||
<< "SeamPlacer: gather and build KD trees with seam candidates: start";
|
||||
|
||||
m_perimeter_points_per_object.emplace(po, po->layer_count());
|
||||
m_perimeter_points_trees_per_object.emplace(po, po->layer_count());
|
||||
@ -376,15 +469,24 @@ void SeamPlacer::init(const Print &print) {
|
||||
[&](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 auto layer = po->get_layer(layer_idx);
|
||||
auto unscaled_z = layer->slice_z;
|
||||
for (const LayerRegion *layer_region : layer->regions()) {
|
||||
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
|
||||
auto polygons = ex_entity->polygons_covered_by_width();
|
||||
Polygons polygons;
|
||||
if (ex_entity->is_collection()) { //collection of inner, outer, and overhang perimeters
|
||||
for (const ExtrusionEntity *perimeter :
|
||||
static_cast<const ExtrusionEntityCollection*>(ex_entity)->entities) {
|
||||
if (perimeter->role() == ExtrusionRole::erExternalPerimeter) {
|
||||
perimeter->polygons_covered_by_width(polygons, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
polygons = ex_entity->polygons_covered_by_width();
|
||||
}
|
||||
for (const auto &poly : polygons) {
|
||||
process_perimeter_polygon(poly, unscaled_z, layer_candidates,
|
||||
enforcer_distance_check, blocker_distance_check);
|
||||
global_model_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -392,35 +494,156 @@ void SeamPlacer::init(const Print &print) {
|
||||
m_perimeter_points_trees_per_object[po][layer_idx] = (std::make_unique<SeamCandidatesTree>(
|
||||
functor, layer_candidates.size()));
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
} );
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: gather and build KD tree with seam candidates: end";
|
||||
<< "SeamPlacer: gather and build KD tree with seam candidates: end";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: gather visibility data into perimeter points : start";
|
||||
<< "SeamPlacer: gather visibility data into perimeter points : start";
|
||||
|
||||
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]) {
|
||||
auto nearby_points = find_nearby_points(hit_points_tree, perimeter_point.m_position,
|
||||
considered_hits_distance);
|
||||
double visibility = 0;
|
||||
for (const auto &hit_point_index : nearby_points) {
|
||||
double distance =
|
||||
(perimeter_point.m_position - hit_points[hit_point_index].m_position).norm();
|
||||
visibility += considered_hits_distance - distance; // The further away from the perimeter point,
|
||||
// the less representative ray hit is
|
||||
}
|
||||
perimeter_point.m_visibility = visibility;
|
||||
perimeter_point.m_visibility = global_model_info.calculate_point_visibility(
|
||||
perimeter_point.m_position, considered_hits_distance);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: gather visibility data into perimeter points : end";
|
||||
<< "SeamPlacer: gather visibility data into perimeter points : end";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: compute overhangs : start";
|
||||
|
||||
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]) {
|
||||
if (layer_idx > 0) {
|
||||
size_t closest_supporter = find_closest_point(
|
||||
*m_perimeter_points_trees_per_object[po][layer_idx - 1],
|
||||
perimeter_point.m_position);
|
||||
const SeamCandidate &supporter_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][layer_idx-1], closest_supporter);
|
||||
const SeamCandidate &prev_point =
|
||||
m_perimeter_points_per_object[po][layer_idx - 1][prev_next.first];
|
||||
const SeamCandidate &next_point =
|
||||
m_perimeter_points_per_object[po][layer_idx - 1][prev_next.second];
|
||||
|
||||
perimeter_point.m_overhang = calculate_overhang(perimeter_point, prev_point,
|
||||
supporter_point, next_point);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: compute overhangs : end";
|
||||
|
||||
for (size_t iteration = 0; iteration < seam_align_iterations; ++iteration) {
|
||||
if (iteration > 0) { //skip this in first iteration, no seam has been picked yet
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: distribute seam positions to other layers : start";
|
||||
|
||||
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) {
|
||||
std::vector<SeamCandidate> &layer_perimeter_points =
|
||||
m_perimeter_points_per_object[po][layer_idx];
|
||||
size_t current = 0;
|
||||
while (current < layer_perimeter_points.size()) {
|
||||
auto seam_position =
|
||||
layer_perimeter_points[layer_perimeter_points[current].m_seam_index].m_position;
|
||||
|
||||
size_t other_layer_idx_start = std::max(
|
||||
(int) layer_idx - (int) seam_align_layer_dist, 0);
|
||||
size_t other_layer_idx_end = std::min(layer_idx + seam_align_layer_dist,
|
||||
m_perimeter_points_per_object[po].size() - 1);
|
||||
|
||||
for (size_t other_layer_idx = other_layer_idx_start;
|
||||
other_layer_idx <= other_layer_idx_end; ++other_layer_idx) {
|
||||
|
||||
size_t closest_point_idx = find_closest_point(
|
||||
*m_perimeter_points_trees_per_object[po][other_layer_idx],
|
||||
seam_position);
|
||||
|
||||
m_perimeter_points_per_object[po][other_layer_idx][closest_point_idx].m_nearby_seam_points->fetch_add(
|
||||
1, std::memory_order_relaxed);
|
||||
|
||||
}
|
||||
|
||||
current += layer_perimeter_points[current].m_polygon_index_reverse + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: distribute seam positions to other layers : end";
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: find seam for each perimeter polygon and store its position in each member of the polygon : start";
|
||||
|
||||
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) {
|
||||
std::vector<SeamCandidate> &layer_perimeter_points =
|
||||
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,
|
||||
current + layer_perimeter_points[current].m_polygon_index_reverse,
|
||||
DefaultSeamComparator{});
|
||||
current += layer_perimeter_points[current].m_polygon_index_reverse + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "SeamPlacer: find seam for each perimeter polygon and store its position in each member of the polygon : end";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SeamPlacer::place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t unscaled_z, int layer_index,
|
||||
bool external_first) {
|
||||
assert(m_perimeter_points_trees_per_object.find(po) != nullptr);
|
||||
assert(m_perimeter_points_per_object.find(po) != nullptr);
|
||||
|
||||
assert(layer_index >= 0);
|
||||
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 Point &fp = loop.first_point();
|
||||
|
||||
//This is backup check, so that slicer does not crash if something weird is going on
|
||||
if (perimeter_points.empty()) {
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "SeamPlacer: Trying to place seam for index which does not contain any outer or overhang perimeter points, maybe new perimeter type option?";
|
||||
loop.split_at(fp, true);
|
||||
} else {
|
||||
auto unscaled_p = unscale(fp);
|
||||
auto closest_perimeter_point_index = find_closest_point(perimeter_points_tree,
|
||||
Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z });
|
||||
size_t perimeter_seam_index = perimeter_points[closest_perimeter_point_index].m_seam_index;
|
||||
Vec3d seam_position = perimeter_points[perimeter_seam_index].m_position;
|
||||
|
||||
Point seam_point = scaled(Vec2d { seam_position.x(), seam_position.y() });
|
||||
|
||||
if (!loop.split_at_vertex(seam_point))
|
||||
// The point is not in the original loop.
|
||||
// Insert it.
|
||||
loop.split_at(seam_point, true);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_FILES
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
@ -436,52 +659,21 @@ void SeamPlacer::init(const Print &print) {
|
||||
fclose(fp);
|
||||
#endif
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: find seam for each perimeter polygon and store its position in each member of the polygon : start";
|
||||
//TODO disable, only debug code
|
||||
#ifdef DEBUG_FILES
|
||||
its_write_obj(triangles, "triangles.obj");
|
||||
|
||||
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) {
|
||||
std::vector<SeamCandidate> &layer_perimeter_points =
|
||||
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,
|
||||
current + layer_perimeter_points[current].m_polygon_index_reverse);
|
||||
current += layer_perimeter_points[current].m_polygon_index_reverse + 1;
|
||||
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";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "PM: find seam for each perimeter polygon and store its position in each member of the polygon : end";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void SeamPlacer::place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t unscaled_z, int layer_index,
|
||||
bool external_first) {
|
||||
assert(m_perimeter_points_trees_per_object.find(po) != nullptr);
|
||||
assert(m_perimeter_points_per_object.find(po) != nullptr);
|
||||
|
||||
assert(layer_index >= 0);
|
||||
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 Point &fp = loop.first_point();
|
||||
|
||||
auto unscaled_p = unscale(fp);
|
||||
auto closest_perimeter_point_index = find_closest_point(perimeter_points_tree,
|
||||
Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z });
|
||||
size_t perimeter_seam_index = perimeter_points[closest_perimeter_point_index].m_seam_index;
|
||||
Vec3d seam_position = perimeter_points[perimeter_seam_index].m_position;
|
||||
|
||||
Point seam_point = scaled(Vec2d { seam_position.x(), seam_position.y() });
|
||||
|
||||
if (!loop.split_at_vertex(seam_point))
|
||||
// The point is not in the original loop.
|
||||
// Insert it.
|
||||
loop.split_at(seam_point, true);
|
||||
}
|
||||
for (size_t i = 0; i < hit_points.size(); ++i)
|
||||
fprintf(fp, "v %f %f %f \n", hit_points[i].m_position[0], hit_points[i].m_position[1],
|
||||
hit_points[i].m_position[2]);
|
||||
fclose(fp);
|
||||
#endif
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
@ -25,7 +26,7 @@ class Grid;
|
||||
|
||||
namespace SeamPlacerImpl {
|
||||
|
||||
enum EnforcedBlockedSeamPoint{
|
||||
enum EnforcedBlockedSeamPoint {
|
||||
BLOCKED = 0,
|
||||
NONE = 1,
|
||||
ENFORCED = 2,
|
||||
@ -33,14 +34,18 @@ enum EnforcedBlockedSeamPoint{
|
||||
|
||||
struct SeamCandidate {
|
||||
SeamCandidate(const Vec3d &pos, size_t polygon_index_reverse, float ccw_angle, EnforcedBlockedSeamPoint type) :
|
||||
m_position(pos), m_visibility(0.0), m_polygon_index_reverse(polygon_index_reverse), m_seam_index(0), m_ccw_angle(
|
||||
m_position(pos), m_visibility(0.0), m_overhang(0.0), m_polygon_index_reverse(polygon_index_reverse), m_seam_index(
|
||||
0), m_ccw_angle(
|
||||
ccw_angle), m_type(type) {
|
||||
m_nearby_seam_points = std::make_unique<std::atomic<size_t>>(0);
|
||||
}
|
||||
Vec3d m_position;
|
||||
float m_visibility;
|
||||
float m_overhang;
|
||||
size_t m_polygon_index_reverse;
|
||||
size_t m_seam_index;
|
||||
float m_ccw_angle;
|
||||
std::unique_ptr<std::atomic<size_t>> m_nearby_seam_points;
|
||||
EnforcedBlockedSeamPoint m_type;
|
||||
};
|
||||
|
||||
@ -74,11 +79,13 @@ class SeamPlacer {
|
||||
public:
|
||||
using SeamCandidatesTree =
|
||||
KDTreeIndirect<3, coordf_t, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
|
||||
const size_t ray_count_per_object = 100000;
|
||||
const double considered_hits_distance = 2.0;
|
||||
static constexpr size_t ray_count_per_object = 200000;
|
||||
static constexpr double considered_hits_distance = 3.0;
|
||||
static constexpr float cosine_hemisphere_sampling_power = 1.5;
|
||||
static constexpr float polygon_angles_arm_distance = 0.6;
|
||||
static constexpr float enforcer_blocker_sqr_distance_tolerance = 0.02;
|
||||
static constexpr float enforcer_blocker_sqr_distance_tolerance = 0.04;
|
||||
static constexpr size_t seam_align_iterations = 3;
|
||||
static constexpr size_t seam_align_layer_dist = 50;
|
||||
//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::unique_ptr<SeamCandidatesTree>>> m_perimeter_points_trees_per_object;
|
||||
|
Loading…
Reference in New Issue
Block a user