Speed up searching for intersections

FIX of O(n^2) complexity!
Remove unneccesary creation empty vectors and testing on empty
Try to remove recursion but result was slower
This commit is contained in:
Filip Sykala - NTB T15p 2023-10-12 10:08:56 +02:00
parent bf6252fbc6
commit 037835065d
2 changed files with 144 additions and 53 deletions

View File

@ -121,45 +121,77 @@ inline std::tuple<int, int> coordinate_aligned_ray_hit_count(size_t
}
template<typename LineType, typename TreeType, typename VectorType>
inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(size_t node_idx,
const TreeType &tree,
const std::vector<LineType> &lines,
const LineType &line,
const typename TreeType::BoundingBox &line_bb)
inline void insert_intersections_with_line(std::vector<std::pair<VectorType, size_t>> &result,
size_t node_idx,
const TreeType &tree,
const std::vector<LineType> &lines,
const LineType &line,
const typename TreeType::BoundingBox &line_bb)
{
const auto &node = tree.node(node_idx);
assert(node.is_valid());
if (node.is_leaf()) {
VectorType intersection_pt;
if (line_alg::intersection(line, lines[node.idx], &intersection_pt)) {
return {std::pair<VectorType, size_t>(intersection_pt, node.idx)};
} else {
return {};
result.emplace_back(intersection_pt, node.idx);
}
} else {
size_t left_node_idx = node_idx * 2 + 1;
size_t right_node_idx = left_node_idx + 1;
const auto &node_left = tree.node(left_node_idx);
const auto &node_right = tree.node(right_node_idx);
assert(node_left.is_valid());
assert(node_right.is_valid());
std::vector<std::pair<VectorType, size_t>> result;
if (node_left.bbox.intersects(line_bb)) {
std::vector<std::pair<VectorType, size_t>> intersections =
get_intersections_with_line<LineType, TreeType, VectorType>(left_node_idx, tree, lines, line, line_bb);
result.insert(result.end(), intersections.begin(), intersections.end());
}
if (node_right.bbox.intersects(line_bb)) {
std::vector<std::pair<VectorType, size_t>> intersections =
get_intersections_with_line<LineType, TreeType, VectorType>(right_node_idx, tree, lines, line, line_bb);
result.insert(result.end(), intersections.begin(), intersections.end());
}
return result;
return;
}
size_t left_node_idx = node_idx * 2 + 1;
size_t right_node_idx = left_node_idx + 1;
const auto &node_left = tree.node(left_node_idx);
const auto &node_right = tree.node(right_node_idx);
assert(node_left.is_valid());
assert(node_right.is_valid());
if (node_left.bbox.intersects(line_bb)) {
insert_intersections_with_line<LineType, TreeType, VectorType>(result, left_node_idx, tree, lines, line, line_bb);
}
if (node_right.bbox.intersects(line_bb)) {
insert_intersections_with_line<LineType, TreeType, VectorType>(result, right_node_idx, tree, lines, line, line_bb);
}
//// NOTE: Non recursive implementation - for my case was slower ;-(
// std::vector<size_t> node_indicies_for_check; // evaluation queue
// size_t approx_size = static_cast<size_t>(std::ceil(std::sqrt(tree.nodes().size())));
// node_indicies_for_check.reserve(approx_size);
// do {
// const auto &node = tree.node(node_index);
// assert(node.is_valid());
// if (node.is_leaf()) {
// VectorType intersection_pt;
// if (line_alg::intersection(line, lines[node.idx], &intersection_pt))
// result.emplace_back(intersection_pt, node.idx);
// node_index = 0;// clear next node
// } else {
// size_t left_node_idx = node_index * 2 + 1;
// size_t right_node_idx = left_node_idx + 1;
// const auto &node_left = tree.node(left_node_idx);
// const auto &node_right = tree.node(right_node_idx);
// assert(node_left.is_valid());
// assert(node_right.is_valid());
// // Set next node index
// node_index = 0; // clear next node
// if (node_left.bbox.intersects(line_bb))
// node_index = left_node_idx;
// if (node_right.bbox.intersects(line_bb)) {
// if (node_index == 0)
// node_index = right_node_idx;
// else
// node_indicies_for_check.push_back(right_node_idx); // store for later evaluation
// }
// }
// if (node_index == 0 && !node_indicies_for_check.empty()) {
// // no direct next node take one from queue
// node_index = node_indicies_for_check.back();
// node_indicies_for_check.pop_back();
// }
//} while (node_index != 0);
}
} // namespace detail
@ -277,7 +309,9 @@ inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(co
auto line_bb = typename TreeType::BoundingBox(line.a, line.a);
line_bb.extend(line.b);
auto intersections = detail::get_intersections_with_line<LineType, TreeType, VectorType>(0, tree, lines, line, line_bb);
std::vector<std::pair<VectorType, size_t>> intersections; // result
detail::insert_intersections_with_line(intersections, 0, tree, lines, line, line_bb);
if (sorted) {
using Floating =
typename std::conditional<std::is_floating_point<typename LineType::Scalar>::value, typename LineType::Scalar, double>::type;
@ -293,7 +327,6 @@ inline std::vector<std::pair<VectorType, size_t>> get_intersections_with_line(co
intersections[i] = points_with_sq_distance[i].second;
}
}
return intersections;
}

View File

@ -5,6 +5,10 @@
#include "IntersectionPoints.hpp"
//#define USE_CGAL_SWEEP_LINE
#define USE_AABB_TREE
//#define USE_AABB_TREE_FLOAT
//#define USE_LINE_TO_LINE
#ifdef USE_CGAL_SWEEP_LINE
#include <CGAL/Cartesian.h>
@ -109,8 +113,64 @@ Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
// use bounding boxes
#include <libslic3r/BoundingBox.hpp>
namespace priv {
//FIXME O(n^2) complexity!
#include <libslic3r/AABBTreeLines.hpp>
namespace{
#ifdef USE_AABB_TREE
// NOTE: it is about 18% slower than USE_LINE_TO_LINE on 'contour_ALIENATO.TTF_glyph_i'
using namespace Slic3r;
Pointfs compute_intersections(const Lines &lines)
{
if (lines.size() < 3)
return {};
auto tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines);
Pointfs result;
for (size_t li = 0; li < lines.size()-1; ++li) {
const Line &l = lines[li];
auto intersections = AABBTreeLines::get_intersections_with_line<false, Point, Line>(lines, tree, l);
for (const auto &[p, node_index] : intersections) {
if (node_index - 1 <= li)
continue;
if (p == l.a || p == l.b)
continue;
result.push_back(p.cast<double>());
}
}
return result;
}
#endif // USE_AABB_TREE
#ifdef USE_AABB_TREE_FLOAT
// NOTE: It is slower than int tree, but has floating point for intersection
using namespace Slic3r;
Pointfs compute_intersections(const Lines &lines)
{
if (lines.size() < 3)
return {};
Linesf input;
input.reserve(lines.size());
for (const Line &line : lines)
input.emplace_back(line.a.cast<double>(), line.b.cast<double>());
auto tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(input);
Pointfs result;
for (size_t li = 0; li < lines.size() - 1; ++li) {
const Linef &l = input[li];
auto intersections = AABBTreeLines::get_intersections_with_line<false, Vec2d, Linef>(input, tree, l);
for (const auto &[p, node_index] : intersections) {
if (node_index - 1 <= li)
continue;
if (p == l.a || p == l.b)
continue;
result.push_back(p.cast<double>());
}
}
return result;
}
#endif // USE_AABB_TREE_FLOAT
#ifdef USE_LINE_TO_LINE
// FIXME O(n^2) complexity!
Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
{
using namespace Slic3r;
@ -123,53 +183,51 @@ Slic3r::Pointfs compute_intersections(const Slic3r::Lines &lines)
Pointfs pts;
Point i;
for (size_t li = 0; li < lines.size(); ++li) {
const Line &l = lines[li];
const Point &a = l.a;
const Point &b = l.b;
Point min(std::min(a.x(), b.x()), std::min(a.y(), b.y()));
Point max(std::max(a.x(), b.x()), std::max(a.y(), b.y()));
BoundingBox bb(min, max);
const Line &l = lines[li];
const Point &a = l.a;
const Point &b = l.b;
BoundingBox bb({a, b});
for (size_t li_ = li + 1; li_ < lines.size(); ++li_) {
const Line &l_ = lines[li_];
const Point &a_ = l_.a;
const Point &b_ = l_.b;
if (a == b_ || b == a_ || a == a_ || b == b_) continue;
Point min_(std::min(a_.x(), b_.x()), std::min(a_.y(), b_.y()));
Point max_(std::max(a_.x(), b_.x()), std::max(a_.y(), b_.y()));
BoundingBox bb_(min_, max_);
// NOTE: Be Carefull - Not only neighbor has same point
if (a == b_ || b == a_ || a == a_ || b == b_)
continue;
BoundingBox bb_({a_, b_});
// intersect of BB compare min max
if (bb.overlap(bb_) &&
l.intersection(l_, &i))
if (bb.overlap(bb_) && l.intersection(l_, &i))
pts.push_back(i.cast<double>());
}
}
return pts;
}
} // namespace priv
#endif // USE_LINE_TO_LINE
} // namespace
Slic3r::Pointfs Slic3r::intersection_points(const Lines &lines)
{
return priv::compute_intersections(lines);
return compute_intersections(lines);
}
Slic3r::Pointfs Slic3r::intersection_points(const Polygon &polygon)
{
return priv::compute_intersections(to_lines(polygon));
return compute_intersections(to_lines(polygon));
}
Slic3r::Pointfs Slic3r::intersection_points(const Polygons &polygons)
{
return priv::compute_intersections(to_lines(polygons));
return compute_intersections(to_lines(polygons));
}
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygon &expolygon)
{
return priv::compute_intersections(to_lines(expolygon));
return compute_intersections(to_lines(expolygon));
}
Slic3r::Pointfs Slic3r::intersection_points(const ExPolygons &expolygons)
{
return priv::compute_intersections(to_lines(expolygons));
return compute_intersections(to_lines(expolygons));
}
#endif // USE_CGAL_SWEEP_LINE