initial demo

fixed KD tree neighbour search, finished refactoring of raycasts,
substantially improved performance of seam placement - seams
are now precomputed and their indexes stored such that place_seam does almost nothing.
This commit is contained in:
PavelMikus 2022-02-02 14:14:55 +01:00
parent 55e0f2dd83
commit 45b49ad545
3 changed files with 143 additions and 83 deletions

View File

@ -82,23 +82,6 @@ Vec3d sample_power_cosine_hemisphere(const Vec2f &samples, float power) {
return Vec3d(cos(term1) * term3, sin(term1) * term3, term2);
}
void resolve_geometry_hit(const igl::Hit &hitpoint,
const indexed_triangle_set &triangles,
const KDTreeIndirect<3, coordf_t, KDTreeCoordinateFunctor> &perimeter_points_tree,
const std::vector<SeamCandidate> &perimeter_points,
std::vector<std::atomic<size_t>> &visibility_counters) {
auto face = triangles.indices[hitpoint.id];
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>();
auto perimeter_point_index = find_closest_point(perimeter_points_tree, hit_pos);
auto perimeter_point_pos = perimeter_points[perimeter_point_index].position;
auto dist_squared = (perimeter_point_pos - hit_pos).squaredNorm();
visibility_counters[perimeter_point_index].fetch_add(1.0 / dist_squared, std::memory_order_relaxed);
}
std::vector<HitInfo> raycast_visibility(size_t ray_count,
const AABBTreeIndirect::Tree<3, float> &raycasting_tree,
const indexed_triangle_set &triangles) {
@ -135,7 +118,7 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
for (size_t index = r.begin(); index < r.end(); ++index) {
Vec3d global_ray_dir = sample_sphere_uniform(global_dir_random_samples[index]);
Vec3d ray_origin = (vision_sphere_center - global_ray_dir * vision_sphere_raidus);
Vec3d local_dir = sample_power_cosine_hemisphere(local_dir_random_samples[index], 1.5);
Vec3d local_dir = sample_power_cosine_hemisphere(local_dir_random_samples[index], 2.0);
Frame f;
f.set_from_z(global_ray_dir);
@ -153,14 +136,14 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
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 surface_normal = edge1.cross(edge2).cast<double>();
Vec3d surface_normal = edge1.cross(edge2).cast<double>().normalized();
init.push_back(HitInfo { hit_pos, surface_normal });
}
}
return init;
},
[](std::vector<HitInfo> left, std::vector<HitInfo> right) {
[](std::vector<HitInfo> left, const std::vector<HitInfo>& right) {
left.insert(left.end(), right.begin(), right.end());
return left;
}
@ -174,10 +157,10 @@ std::vector<HitInfo> raycast_visibility(size_t ray_count,
its_write_obj(triangles, "triangles.obj");
Slic3r::CNumericLocalesSetter locales_setter;
FILE *fp = boost::nowide::fopen("perimeter.obj", "w");
FILE *fp = boost::nowide::fopen("hits.obj", "w");
if (fp == nullptr) {
BOOST_LOG_TRIVIAL(error)
<< "Couldn't open " << "perimeter.obj" << " for writing";
<< "Couldn't open " << "hits.obj" << " for writing";
}
for (size_t i = 0; i < hit_points.size(); ++i)
@ -189,6 +172,30 @@ 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) {
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);
}
}
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;
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) {
min_visibility = perimeter_points[index].m_visibility;
seam_index = index;
}
}
for (size_t index = start_index; index <= end_index; ++index) {
perimeter_points[index].m_seam_index = seam_index;
}
}
} // namespace SeamPlacerImpl
void SeamPlacer::init(const Print &print) {
@ -223,45 +230,109 @@ void SeamPlacer::init(const Print &print) {
const auto layer = po->layers()[index];
auto unscaled_z = layer->slice_z;
for (const ExPolygon &expoly : layer->lslices) {
// Contour - insert first point marked as Polygon
// start, then insert rest sequentially.
{
auto unscaled_p = unscale(expoly.contour[0]);
init.emplace_back(Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z }, true);
}
for (size_t index = 1; index < expoly.contour.size(); ++index) {
auto unscaled_p = unscale(expoly.contour[index]);
init.emplace_back(Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z });
}
process_perimeter_polygon(expoly.contour, unscaled_z, init);
for (const Polygon &hole : expoly.holes) {
// Perform the same for each hole
{
auto unscaled_p = unscale(hole[0]);
init.emplace_back(Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z }, true);
}
for (size_t index = 1; index < hole.size(); ++index) {
auto unscaled_p = unscale(hole[index]);
init.emplace_back(Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z });
}
process_perimeter_polygon(hole, unscaled_z, init);
}
}
}
return init;
},
[](std::vector<SeamCandidate> prev, std::vector<SeamCandidate> next) {
[](std::vector<SeamCandidate> prev, const std::vector<SeamCandidate> &next) {
prev.insert(prev.end(), next.begin(), next.end());
return prev;
});
auto &perimeter_points = m_perimeter_points_per_object[po];
BOOST_LOG_TRIVIAL(debug)
<< "PM: gather and build KD tree with seam candidates: end";
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::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;
};
auto nearby_points = find_nearby_points(hit_points_tree, perimeter_point.m_position,
considered_hits_distance, filter);
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;
}
});
//TODO disable, only debug code
//#ifdef 0
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].m_position[0], perimeter_points[i].m_position[1],
perimeter_points[i].m_position[2], perimeter_points[i].m_visibility);
fclose(fp);
//#endif
// 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;
// SeamPlacer::PointTree &perimeter_points_tree = m_perimeter_points_trees_per_object.find(po)->second;
BOOST_LOG_TRIVIAL(debug)
<< "PM: gather and build KD tree with seam candidates: end";
<< "PM: gather visibility data into perimeter points : end";
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::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.
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;
}
});
//Above parallel_for does not consider the first perimeter polygon, so do 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";
}
}
@ -273,29 +344,20 @@ void SeamPlacer::place_seam(const PrintObject *po, ExtrusionLoop &loop, coordf_t
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;
Points loop_points { };
loop.collect_points(loop_points);
const Point &fp = loop.first_point();
// vector of pairs: first-> index into perimeter points, second-> index into loop points
auto closest_perimeter_point_indices = std::vector<std::pair<size_t, size_t>>(loop_points.size());
for (size_t p_index = 0; p_index < loop_points.size(); ++p_index) {
auto unscaled_p = unscale(loop_points[p_index]);
closest_perimeter_point_indices.emplace_back(find_closest_point(perimeter_points_tree,
Vec3d { unscaled_p.x(), unscaled_p.y(), unscaled_z }), p_index);
}
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;
std::sort(closest_perimeter_point_indices.begin(), closest_perimeter_point_indices.end(),
[&](const std::pair<size_t, size_t> left, const std::pair<size_t, size_t> right) {
return perimeter_points[left.first].visibility < perimeter_points[right.first].visibility;
});
Point seam_point = scaled(Vec2d { seam_position.x(), seam_position.y() });
loop.split_at_vertex(loop_points[closest_perimeter_point_indices[0].second]);
//
// Point seam = last_pos;
// if (!loop.split_at_vertex(seam))
// // The point is not in the original loop.
// // Insert it.
// loop.split_at(seam, true);
if (!loop.split_at_vertex(seam_point))
// The point is not in the original loop.
// Insert it.
loop.split_at(seam_point, true);
}
} // namespace Slic3r

View File

@ -25,15 +25,13 @@ class Grid;
namespace SeamPlacerImpl {
struct SeamCandidate {
explicit SeamCandidate(const Vec3d &pos) :
position(pos), visibility(0.0), polygon_start(false) {
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, bool polygon_start) :
position(pos), visibility(0.0), polygon_start(polygon_start) {
}
Vec3d position;
float visibility;
bool polygon_start;
Vec3d m_position;
float m_visibility;
size_t m_polygon_index_reverse;
size_t m_seam_index;
};
struct HitInfo {
@ -41,14 +39,13 @@ struct HitInfo {
Vec3d m_surface_normal;
};
struct KDTreeCoordinateFunctor {
KDTreeCoordinateFunctor(std::vector<SeamCandidate> *seam_candidates) :
seam_candidates(seam_candidates) {
}
std::vector<SeamCandidate> *seam_candidates;
float operator()(size_t index, size_t dim) const {
return seam_candidates->operator[](index).position[dim];
return seam_candidates->operator[](index).m_position[dim];
}
};
@ -67,6 +64,7 @@ class SeamPlacer {
using PointTree =
KDTreeIndirect<3, coordf_t, SeamPlacerImpl::KDTreeCoordinateFunctor>;
const size_t ray_count_per_object = 100000;
const double considered_hits_distance = 4.0;
public:
std::unordered_map<const PrintObject*, PointTree> m_perimeter_points_trees_per_object;

View File

@ -231,21 +231,21 @@ size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& poi
return find_closest_point(kdtree, point, [](size_t) { return true; });
}
// Find a nearby points (spherical neighbourhood) using Euclidian metrics.
// Find nearby points (spherical neighbourhood) using Euclidian metrics.
template<typename KDTreeIndirectType, typename PointType, typename FilterFn>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center,
typename KDTreeIndirectType::CoordType& max_distance, FilterFn filter)
const typename KDTreeIndirectType::CoordType& max_distance, FilterFn filter)
{
using CoordType = typename KDTreeIndirectType::CoordType;
struct Visitor {
const KDTreeIndirectType &kdtree;
const PointType &center;
const CoordType &max_distance_squared;
const PointType center;
const CoordType max_distance_squared;
const FilterFn filter;
std::vector<size_t> result;
Visitor(const KDTreeIndirectType &kdtree, const PointType &center, const CoordType &max_distance,
Visitor(const KDTreeIndirectType &kdtree, const PointType& center, const CoordType &max_distance,
FilterFn filter) :
kdtree(kdtree), center(center), max_distance_squared(max_distance*max_distance), filter(filter) {
}
@ -270,7 +270,7 @@ std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree, const P
template<typename KDTreeIndirectType, typename PointType>
std::vector<size_t> find_nearby_points(const KDTreeIndirectType &kdtree, const PointType &center,
typename KDTreeIndirectType::CoordType& max_distance)
const typename KDTreeIndirectType::CoordType& max_distance)
{
return find_nearby_points(kdtree, center, max_distance, [](size_t) {
return true;