Change grid to KD tree for fast search of support points in max radius

This commit is contained in:
Filip Sykala - NTB T15p 2024-09-18 15:29:49 +02:00 committed by Lukas Matena
parent 9f97d9252c
commit d69177f910
2 changed files with 73 additions and 84 deletions

View File

@ -38,9 +38,10 @@ public:
KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(indices); } KDTreeIndirect(CoordinateFn coordinate, std::vector<size_t> indices) : coordinate(coordinate) { this->build(indices); }
KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); } KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); }
KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {} KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {}
KDTreeIndirect(const KDTreeIndirect &rhs) : m_nodes(rhs.m_nodes), coordinate(rhs.coordinate) {}
KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; } KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; }
void clear() { m_nodes.clear(); } void clear() { m_nodes.clear(); }
const std::vector<size_t> &get_nodes() const { return m_nodes; }
void build(size_t num_indices) void build(size_t num_indices)
{ {
std::vector<size_t> indices; std::vector<size_t> indices;

View File

@ -5,93 +5,81 @@
#include "SupportPointGenerator.hpp" #include "SupportPointGenerator.hpp"
#include <unordered_map> // point grid
#include "libslic3r/Execution/ExecutionTBB.hpp" // parallel preparation of data for sampling #include "libslic3r/Execution/ExecutionTBB.hpp" // parallel preparation of data for sampling
#include "libslic3r/Execution/Execution.hpp" #include "libslic3r/Execution/Execution.hpp"
#include "libslic3r/KDTreeIndirect.hpp"
using namespace Slic3r; using namespace Slic3r;
using namespace Slic3r::sla; using namespace Slic3r::sla;
namespace { namespace {
/// <summary> /// <summary>
/// Struct to store support points in 2d grid to faster search for nearest support points /// Struct to store support points in KD tree to fast search for nearest ones.
/// </summary> /// </summary>
class Grid2D class NearPoints
{ {
coord_t m_cell_size; // Squar: x and y are same struct PointAccessor {
coord_t m_cell_size_half; // multiple trees points into same data storage of all support points
LayerSupportPoints *m_supports_ptr;
explicit PointAccessor(LayerSupportPoints *supports_ptr) : m_supports_ptr(supports_ptr) {}
// accessor to coordinate for kd tree
const coord_t &operator()(size_t idx, size_t dimension) const {
return m_supports_ptr->at(idx).position_on_layer[dimension];
}
};
using Key = Point; // scaled point PointAccessor m_points;
using Value = size_t; // index into m_supports_ptr using Tree = KDTreeIndirect<2, coord_t, PointAccessor>;
struct GridHash{std::size_t operator()(const Key &cell_id) const { Tree m_tree;
return std::hash<int>()(cell_id.x()) ^ std::hash<int>()(cell_id.y() * 593);
}};
using Grid = std::unordered_multimap<Key, Value, GridHash>;
Grid m_grid;
// multiple grids points into same data storage of support points
LayerSupportPoints *m_supports_ptr;
public: public:
/// <summary> /// <summary>
/// Set cell size for grid /// Constructor get pointer on the global storage of support points
/// </summary> /// </summary>
/// <param name="cell_size">Granularity of stored points
/// Must be bigger than maximal used radius</param>
/// <param name="supports_ptr">Pointer on Support vector</param> /// <param name="supports_ptr">Pointer on Support vector</param>
explicit Grid2D(const coord_t &cell_size, LayerSupportPoints* supports_ptr) explicit NearPoints(LayerSupportPoints* supports_ptr)
: m_cell_size(cell_size), m_cell_size_half(cell_size / 2), m_supports_ptr(supports_ptr) {} : m_points(supports_ptr), m_tree(m_points) {}
Key cell_id(const Point &point) const {
Key::coord_type x = point.x() / m_cell_size;
Key::coord_type y = point.y() / m_cell_size;
// correction around zero => -1 / 5 = 0
if (point.x() < 0) --x;
if (point.y() < 0) --y;
return Key{x, y};
}
void add(LayerSupportPoint &&point) { void add(LayerSupportPoint &&point) {
size_t index = m_supports_ptr->size(); LayerSupportPoints &pts = *m_points.m_supports_ptr;
m_supports_ptr->emplace_back(std::move(point)); size_t index = pts.size();
m_grid.emplace(cell_id(point.position_on_layer), index); pts.emplace_back(std::move(point));
// IMPROVE: only add to existing tree, do not reconstruct tree
std::vector<size_t> indices = m_tree.get_nodes(); // copy
auto it = std::remove_if(indices.begin(), indices.end(),
[index](size_t i) { return i >= index; });
indices.erase(it, indices.end());
indices.push_back(index);
m_tree.clear();
m_tree.build(indices); // consume indices
} }
using CheckFnc = std::function<bool(const LayerSupportPoint &, const Point&)>; using CheckFnc = std::function<bool(const LayerSupportPoint &, const Point&)>;
bool exist_true_in_4cell_neighbor(const Point &pos, const CheckFnc& fnc) const { bool exist_true_in_radius(const Point &pos, coord_t radius, const CheckFnc &fnc) const {
// TODO: remove - test all support points without grid speed up std::vector<size_t> point_indices = find_nearby_points(m_tree, pos, radius);
for (const auto &[key, value]: m_grid) return std::any_of(point_indices.begin(), point_indices.end(),
if(fnc(m_supports_ptr->at(value), pos)) [&points = *m_points.m_supports_ptr, &pos, &fnc](size_t point_index){
return true; return fnc(points.at(point_index), pos);
return false; });
Key key = cell_id(pos);
if (exist_true_for_cell(key, pos, fnc)) return true;
Point un_cell_pos(
key.x() * m_cell_size + m_cell_size_half,
key.y() * m_cell_size + m_cell_size_half );
Key key2(
(un_cell_pos.x() > pos.x()) ? key.x() + 1 : key.x() - 1,
(un_cell_pos.y() > pos.y()) ? key.y() + 1 : key.y() - 1);
if (exist_true_for_cell(key2, pos, fnc)) return true;
if (exist_true_for_cell({key.x(), key2.y()}, pos, fnc)) return true;
if (exist_true_for_cell({key2.x(), key.y()}, pos, fnc)) return true;
return false;
} }
void merge(Grid2D &&grid) { void merge(NearPoints &&near_point) {
// support to merge only grid with same size // need to have same global storage of support points
assert(m_cell_size == grid.m_cell_size); assert(m_points.m_supports_ptr == near_point.m_points.m_supports_ptr);
m_grid.merge(std::move(grid.m_grid));
} // IMPROVE: merge trees instead of rebuild
private: std::vector<size_t> indices = m_tree.get_nodes(); // copy
bool exist_true_for_cell(const Key &key, const Point &pos, const CheckFnc& fnc) const{ const std::vector<size_t>& indices2 = near_point.m_tree.get_nodes();
auto [begin_it, end_it] = m_grid.equal_range(key); indices.insert(indices.end(), indices2.begin(), indices2.end());
for (Grid::const_iterator it = begin_it; it != end_it; ++it) { auto it = std::remove_if(indices.begin(), indices.end(),
const LayerSupportPoint &support_point = m_supports_ptr->at(it->second); [size = indices.size()](size_t i) { return i >= size; });
if (fnc(support_point, pos)) indices.erase(it, indices.end());
return true; // remove duplicit indices
} std::sort(indices.begin(), indices.end());
return false; it = std::unique(indices.begin(), indices.end());
indices.erase(it, indices.end());
// rebuild tree
m_tree.clear();
m_tree.build(indices); // consume indices
} }
}; };
@ -146,14 +134,14 @@ Point intersection(const Point &p1, const Point &p2, const Point &cnt, double r2
/// <param name="part">Current layer part to process</param> /// <param name="part">Current layer part to process</param>
/// <param name="prev_grids">Grids which will be moved to current grid</param> /// <param name="prev_grids">Grids which will be moved to current grid</param>
/// <returns>Grid for given part</returns> /// <returns>Grid for given part</returns>
Grid2D create_part_grid( NearPoints create_part_grid(
const LayerParts &prev_layer_parts, const LayerParts &prev_layer_parts,
const LayerPart &part, const LayerPart &part,
std::vector<Grid2D> &prev_grids std::vector<NearPoints> &prev_grids
) { ) {
const LayerParts::const_iterator &prev_part_it = part.prev_parts.front().part_it; const LayerParts::const_iterator &prev_part_it = part.prev_parts.front().part_it;
size_t index_of_prev_part = prev_part_it - prev_layer_parts.begin(); size_t index_of_prev_part = prev_part_it - prev_layer_parts.begin();
Grid2D part_grid = (prev_part_it->next_parts.size() == 1)? NearPoints part_grid = (prev_part_it->next_parts.size() == 1)?
std::move(prev_grids[index_of_prev_part]) : std::move(prev_grids[index_of_prev_part]) :
// Need a copy there are multiple parts above previus one // Need a copy there are multiple parts above previus one
prev_grids[index_of_prev_part]; // copy prev_grids[index_of_prev_part]; // copy
@ -165,7 +153,7 @@ Grid2D create_part_grid(
if (prev_part_it->next_parts.size() == 1) { if (prev_part_it->next_parts.size() == 1) {
part_grid.merge(std::move(prev_grids[index_of_prev_part])); part_grid.merge(std::move(prev_grids[index_of_prev_part]));
} else { // Need a copy there are multiple parts above previus one } else { // Need a copy there are multiple parts above previus one
Grid2D grid_ = prev_grids[index_of_prev_part]; // copy NearPoints grid_ = prev_grids[index_of_prev_part]; // copy
part_grid.merge(std::move(grid_)); part_grid.merge(std::move(grid_));
} }
} }
@ -179,13 +167,15 @@ Grid2D create_part_grid(
/// <param name="config">Configuration to sample</param> /// <param name="config">Configuration to sample</param>
/// <param name="part_grid">Keep previous sampled suppport points</param> /// <param name="part_grid">Keep previous sampled suppport points</param>
/// <param name="part_z">current z coordinate of part</param> /// <param name="part_z">current z coordinate of part</param>
/// <param name="maximal_radius">Max distance to seach support for sample</param>
void support_part_overhangs( void support_part_overhangs(
const LayerPart &part, const LayerPart &part,
const SupportPointGeneratorConfig &config, const SupportPointGeneratorConfig &config,
Grid2D &part_grid, NearPoints &part_grid,
float part_z float part_z,
coord_t maximal_radius
) { ) {
Grid2D::CheckFnc is_supported = [] NearPoints::CheckFnc is_supported = []
(const LayerSupportPoint &support_point, const Point &p) -> bool { (const LayerSupportPoint &support_point, const Point &p) -> bool {
// Debug visualization of all sampled outline // Debug visualization of all sampled outline
//return false; //return false;
@ -199,10 +189,8 @@ void support_part_overhangs(
return dp.cast<double>().squaredNorm() < r2; return dp.cast<double>().squaredNorm() < r2;
}; };
// check distance to nearest support points from grid
float maximal_radius = scale_(5.f);
for (const Point &p : part.samples) { for (const Point &p : part.samples) {
if (!part_grid.exist_true_in_4cell_neighbor(p, is_supported)) { if (!part_grid.exist_true_in_radius(p, maximal_radius, is_supported)) {
// not supported sample, soo create new support point // not supported sample, soo create new support point
part_grid.add(LayerSupportPoint{ part_grid.add(LayerSupportPoint{
SupportPoint{ SupportPoint{
@ -232,7 +220,7 @@ Points uniformly_sample(const ExPolygon &island, const SupportPointGeneratorConf
/// <param name="part_grid">OUT place to store new supports</param> /// <param name="part_grid">OUT place to store new supports</param>
/// <param name="part_z">z coordinate of part</param> /// <param name="part_z">z coordinate of part</param>
/// <param name="cfg"></param> /// <param name="cfg"></param>
void support_island(const LayerPart &part, Grid2D& part_grid, float part_z, void support_island(const LayerPart &part, NearPoints& part_grid, float part_z,
const SupportPointGeneratorConfig &cfg) { const SupportPointGeneratorConfig &cfg) {
Points pts = uniformly_sample(*part.shape, cfg); Points pts = uniformly_sample(*part.shape, cfg);
for (const Point &pt : pts) for (const Point &pt : pts)
@ -565,28 +553,28 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
const_cast<SupportPointGeneratorConfig &>(config).support_curve = load_curve_from_file(); const_cast<SupportPointGeneratorConfig &>(config).support_curve = load_curve_from_file();
// Maximal radius of supported area of one support point // Maximal radius of supported area of one support point
double max_support_radius = config.support_curve.back().x(); // cfg.cell_size; double max_support_radius = config.support_curve.back().x();
// maximal radius of support // check distance to nearest support points from grid
coord_t cell_size = scale_(2*max_support_radius); coord_t maximal_radius = static_cast<coord_t>(scale_(max_support_radius));
// Storage for support points used by grid // Storage for support points used by grid
LayerSupportPoints result; LayerSupportPoints result;
// grid index == part in layer index // grid index == part in layer index
std::vector<Grid2D> prev_grids; // same count as previous layer item size std::vector<NearPoints> prev_grids; // same count as previous layer item size
for (size_t layer_id = 0; layer_id < layers.size(); ++layer_id) { for (size_t layer_id = 0; layer_id < layers.size(); ++layer_id) {
const Layer &layer = layers[layer_id]; const Layer &layer = layers[layer_id];
prepare_supports_for_layer(result, layer.print_z, config); prepare_supports_for_layer(result, layer.print_z, config);
// grid index == part in layer index // grid index == part in layer index
std::vector<Grid2D> grids; std::vector<NearPoints> grids;
grids.reserve(layer.parts.size()); grids.reserve(layer.parts.size());
for (const LayerPart &part : layer.parts) { for (const LayerPart &part : layer.parts) {
if (part.prev_parts.empty()) { if (part.prev_parts.empty()) {
// only island add new grid // only island add new grid
grids.emplace_back(cell_size, &result); grids.emplace_back(&result);
// new island - needs support no doubt // new island - needs support no doubt
support_island(part, grids.back(), layer.print_z, config); support_island(part, grids.back(), layer.print_z, config);
} else { } else {
@ -594,7 +582,7 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
assert(layer_id != 0); assert(layer_id != 0);
const LayerParts &prev_layer_parts = layers[layer_id - 1].parts; const LayerParts &prev_layer_parts = layers[layer_id - 1].parts;
grids.push_back(create_part_grid(prev_layer_parts, part, prev_grids)); grids.push_back(create_part_grid(prev_layer_parts, part, prev_grids));
support_part_overhangs(part, config, grids.back(), layer.print_z); support_part_overhangs(part, config, grids.back(), layer.print_z, maximal_radius);
} }
} }
prev_grids = std::move(grids); prev_grids = std::move(grids);