integration of enforcers and blockers

This commit is contained in:
PavelMikus 2022-02-04 16:22:30 +01:00
parent 38a6e231f2
commit 53e9bb3ebf
3 changed files with 233 additions and 98 deletions

View File

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

View File

@ -91,8 +91,8 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
// 0.05 added to avoid corner cases
// Prepare random samples per ray
std::random_device rnd_device;
std::mt19937 mersenne_engine { rnd_device() };
// std::random_device rnd_device;
std::mt19937 mersenne_engine { 12345 };
std::uniform_real_distribution<float> dist { 0, 1 };
auto gen = [&dist, &mersenne_engine]() {
@ -125,7 +125,7 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
Vec3d final_ray_dir = (f.to_world(local_dir));
igl::Hit hitpoint;
// FIXME: This query will not compile for float ray origin and
// FIXME: This AABBTTreeIndirect query will not compile for float ray origin and
// direction for some reason
auto hit = AABBTreeIndirect::intersect_ray_first_hit(triangles.vertices,
triangles.indices, raycasting_tree, ray_origin, final_ray_dir, hitpoint);
@ -135,7 +135,8 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
auto edge1 = triangles.vertices[face[1]] - triangles.vertices[face[0]];
auto edge2 = triangles.vertices[face[2]] - triangles.vertices[face[0]];
Vec3d hit_pos = (triangles.vertices[face[0]] + edge1 * hitpoint.u + edge2 * hitpoint.v).cast<double>();
Vec3d hit_pos = (triangles.vertices[face[0]] + edge1 * hitpoint.u + edge2 * hitpoint.v).cast<
double>();
Vec3d surface_normal = edge1.cross(edge2).cast<double>().normalized();
init.push_back(HitInfo { hit_pos, surface_normal });
@ -172,20 +173,130 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
return hit_points;
}
void process_perimeter_polygon(const Polygon &polygon, coordf_t z_coord, std::vector<SeamCandidate> &result_vec) {
std::vector<float> calculate_polygon_angles_at_vertices(const Polygon &polygon, const std::vector<float> &lengths,
float min_arm_length)
{
assert(polygon.points.size() + 1 == lengths.size());
if (min_arm_length > 0.25f * lengths.back())
min_arm_length = 0.25f * lengths.back();
// Find the initial prev / next point span.
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);
for (; idx_curr < polygon.points.size(); ++idx_curr) {
// Move idx_prev up until the distance between idx_prev and idx_curr is lower than min_arm_length.
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)
++idx_prev;
// Move idx_prev one step back.
if (idx_prev == 0)
idx_prev = polygon.points.size() - 1;
else
--idx_prev;
// Move idx_next up until the distance between idx_curr and idx_next is greater than min_arm_length.
if (idx_curr <= idx_next) {
while (idx_next < polygon.points.size() && lengths[idx_next] - lengths[idx_curr] < min_arm_length)
++idx_next;
if (idx_next == polygon.points.size())
idx_next = 0;
}
while (idx_next < idx_curr && lengths.back() - lengths[idx_curr] + lengths[idx_next] < min_arm_length)
++idx_next;
// Calculate angle between idx_prev, idx_curr, idx_next.
const Point &p0 = polygon.points[idx_prev];
const Point &p1 = polygon.points[idx_curr];
const Point &p2 = polygon.points[idx_next];
const Point v1 = p1 - p0;
const Point v2 = p2 - p1;
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));
float angle = float(atan2(double(cross), double(dot)));
angles[idx_curr] = angle;
}
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) {
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);
Vec3d last_blocker_checked_point { 0, 0, -1 };
double blocker_dist_sqr = blocker_distance_check(last_blocker_checked_point);
for (size_t index = 0; index < polygon.size(); ++index) {
Vec2d unscaled_p = unscale(polygon[index]);
Vec3d unscaled_position = Vec3d { unscaled_p.x(), unscaled_p.y(), z_coord };
result_vec.emplace_back(unscaled_position, polygon.size() - index - 1);
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
// so as long as we are at least enforcer_blocker_distance_tolerance deep in that area, and the enforcer distance is greater than
// enforcer_blocker_distance_tolerance, we are fine.
if (enforcer_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance
||
(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);
last_enforcer_checked_point = unscaled_position;
if (enforcer_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance) {
type = EnforcedBlockedSeamPoint::ENFORCED;
}
}
}
//same for blockers
if (blocker_dist_sqr >= 0) {
if (blocker_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance
||
(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);
last_blocker_checked_point = unscaled_position;
if (blocker_dist_sqr < SeamPlacer::enforcer_blocker_sqr_distance_tolerance) {
type = EnforcedBlockedSeamPoint::BLOCKED;
}
}
}
result_vec.emplace_back(unscaled_position, polygon.size() - index - 1, ccw_angle, type);
}
}
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;
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) {
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;
seam_index = index;
}
}
@ -193,7 +304,6 @@ void pick_seam_point(std::vector<SeamCandidate> &perimeter_points, size_t start_
for (size_t index = start_index; index <= end_index; ++index) {
perimeter_points[index].m_seam_index = seam_index;
}
}
} // namespace SeamPlacerImpl
@ -217,32 +327,73 @@ void SeamPlacer::init(const Print &print) {
BOOST_LOG_TRIVIAL(debug)
<< "PM: build AABB tree for raycasting: end";
BOOST_LOG_TRIVIAL(debug)
<< "PM: 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_transform(enforcers, obj_transform);
its_transform(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 &center) {
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 &center) {
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);
};
BOOST_LOG_TRIVIAL(debug)
<< "PM: 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() };
BOOST_LOG_TRIVIAL(debug)
<< "PM: gather and build KD tree with seam candidates: start";
// gather seam candidates (perimeter points)
m_perimeter_points_per_object[po] = tbb::parallel_reduce(tbb::blocked_range<size_t>(0, po->layers().size()),
std::vector<SeamCandidate> { }, [&](tbb::blocked_range<size_t> r, std::vector<SeamCandidate> init) {
for (size_t index = r.begin(); index < r.end(); ++index) {
const auto layer = po->layers()[index];
<< "PM: 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());
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 auto layer = po->get_layer(layer_idx);
auto unscaled_z = layer->slice_z;
for (const ExPolygon &expoly : layer->lslices) {
process_perimeter_polygon(expoly.contour, unscaled_z, init);
for (const Polygon &hole : expoly.holes) {
process_perimeter_polygon(hole, unscaled_z, init);
for (const LayerRegion *layer_region : layer->regions()) {
for (const ExtrusionEntity *ex_entity : layer_region->perimeters.entities) {
auto 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);
}
}
}
return init;
},
[](std::vector<SeamCandidate> prev, const std::vector<SeamCandidate> &next) {
prev.insert(prev.end(), next.begin(), next.end());
return prev;
auto functor = SeamCandidateCoordinateFunctor { &layer_candidates };
m_perimeter_points_trees_per_object[po][layer_idx] = (std::make_unique<SeamCandidatesTree>(
functor, layer_candidates.size()));
}
});
auto &perimeter_points = m_perimeter_points_per_object[po];
BOOST_LOG_TRIVIAL(debug)
<< "PM: gather and build KD tree with seam candidates: end";
@ -250,18 +401,10 @@ void SeamPlacer::init(const Print &print) {
BOOST_LOG_TRIVIAL(debug)
<< "PM: gather visibility data into perimeter points : start";
tbb::parallel_for(tbb::blocked_range<size_t>(0, perimeter_points.size()),
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 index = r.begin(); index < r.end(); ++index) {
SeamCandidate &perimeter_point = perimeter_points[index];
// auto filter = [&](size_t hit_point_index) {
// const HitInfo &hit_point = hit_points[hit_point_index];
// Vec3d tolerance_center = hit_point.m_position - hit_point.m_surface_normal * EPSILON;
// double signed_distance_from_hit_place = (perimeter_point.m_position - tolerance_center).dot(
// hit_point.m_surface_normal);
// return signed_distance_from_hit_place >= 0 && signed_distance_from_hit_place <= 1.0;
// };
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;
@ -273,8 +416,12 @@ void SeamPlacer::init(const Print &print) {
}
perimeter_point.m_visibility = visibility;
}
}
});
BOOST_LOG_TRIVIAL(debug)
<< "PM: gather visibility data into perimeter points : end";
#ifdef DEBUG_FILES
Slic3r::CNumericLocalesSetter locales_setter;
FILE *fp = boost::nowide::fopen("perimeters.obj", "w");
@ -289,45 +436,22 @@ void SeamPlacer::init(const Print &print) {
fclose(fp);
#endif
BOOST_LOG_TRIVIAL(debug)
<< "PM: gather visibility data into perimeter points : end";
// Build KD tree with seam candidates
auto functor = KDTreeCoordinateFunctor { &perimeter_points };
m_perimeter_points_trees_per_object.emplace(std::piecewise_construct, std::forward_as_tuple(po),
std::forward_as_tuple(functor, m_perimeter_points_per_object[po].size()));
// SeamPlacer::PointTree &perimeter_points_tree = m_perimeter_points_trees_per_object.find(po)->second;
BOOST_LOG_TRIVIAL(debug)
<< "PM: find seam for each perimeter polygon and store its position in each member of the polygon : start";
assert(perimeter_points.back().m_polygon_index_reverse == 0);
tbb::parallel_for(tbb::blocked_range<size_t>(0, perimeter_points.size()),
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_perimeter_points_per_object[po].size()),
[&](tbb::blocked_range<size_t> r) {
//find nearest next perimeter polygon start
size_t start = r.begin();
while (perimeter_points[start].m_polygon_index_reverse != 0) {
start++;
};
start++;
if (start == perimeter_points.size())
return;
//find nearest polygon end after range end; The perimeter_points must end with point with index 0
// start at r.end() -1, because tbb uses exlusive range, and we want inclusive range
size_t end = r.end() - 1;
while (perimeter_points[end].m_polygon_index_reverse != 0) {
end++;
};
while (start <= end) {
pick_seam_point(perimeter_points, start,
start + perimeter_points[start].m_polygon_index_reverse);
start += perimeter_points[start].m_polygon_index_reverse + 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()) {
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;
}
}
});
//Above parallel_for does not consider the first perimeter polygon, so add it additionally
pick_seam_point(perimeter_points, 0, perimeter_points[0].m_polygon_index_reverse);
BOOST_LOG_TRIVIAL(debug)
<< "PM: find seam for each perimeter polygon and store its position in each member of the polygon : end";
@ -335,13 +459,14 @@ void SeamPlacer::init(const Print &print) {
}
}
void SeamPlacer::place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t unscaled_z, const Point &last_pos,
bool external_first,
double nozzle_diameter, const EdgeGrid::Grid *lower_layer_edge_grid) {
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);
const auto &perimeter_points_tree = m_perimeter_points_trees_per_object.find(po)->second;
const auto &perimeter_points = m_perimeter_points_per_object.find(po)->second;
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();

View File

@ -3,6 +3,7 @@
#include <optional>
#include <vector>
#include <memory>
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/Polygon.hpp"
@ -24,14 +25,23 @@ class Grid;
namespace SeamPlacerImpl {
enum EnforcedBlockedSeamPoint{
BLOCKED = 0,
NONE = 1,
ENFORCED = 2,
};
struct SeamCandidate {
SeamCandidate(const Vec3d &pos, size_t polygon_index_reverse) :
m_position(pos), m_visibility(0.0), m_polygon_index_reverse(polygon_index_reverse), m_seam_index(0) {
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(
ccw_angle), m_type(type) {
}
Vec3d m_position;
float m_visibility;
size_t m_polygon_index_reverse;
size_t m_seam_index;
float m_ccw_angle;
EnforcedBlockedSeamPoint m_type;
};
struct HitInfo {
@ -39,8 +49,8 @@ struct HitInfo {
Vec3d m_surface_normal;
};
struct KDTreeCoordinateFunctor {
KDTreeCoordinateFunctor(std::vector<SeamCandidate> *seam_candidates) :
struct SeamCandidateCoordinateFunctor {
SeamCandidateCoordinateFunctor(std::vector<SeamCandidate> *seam_candidates) :
seam_candidates(seam_candidates) {
}
std::vector<SeamCandidate> *seam_candidates;
@ -61,21 +71,22 @@ struct HitInfoCoordinateFunctor {
} // namespace SeamPlacerImpl
class SeamPlacer {
using PointTree =
KDTreeIndirect<3, coordf_t, SeamPlacerImpl::KDTreeCoordinateFunctor>;
const size_t ray_count_per_object = 150000;
const double considered_hits_distance = 2.0;
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 float cosine_hemisphere_sampling_power = 1.5;
std::unordered_map<const PrintObject*, PointTree> m_perimeter_points_trees_per_object;
std::unordered_map<const PrintObject*, std::vector<SeamPlacerImpl::SeamCandidate>> m_perimeter_points_per_object;
static constexpr float polygon_angles_arm_distance = 0.6;
static constexpr float enforcer_blocker_sqr_distance_tolerance = 0.02;
//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;
void init(const Print &print);
void place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t unscaled_z, const Point &last_pos,
bool external_first,
double nozzle_diameter, const EdgeGrid::Grid *lower_layer_edge_grid);
void place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t unscaled_z, int layer_index,
bool external_first);
};
} // namespace Slic3r