Fix AABB tree query, add new fast query for point outside, which uses axis aligned rays

This commit is contained in:
PavelMikus 2022-11-25 15:57:59 +01:00 committed by Pavel Mikuš
parent f4f1958cac
commit 11b03804dc
2 changed files with 144 additions and 59 deletions

View File

@ -8,78 +8,121 @@
#include <type_traits>
#include <vector>
namespace Slic3r {
namespace AABBTreeLines {
namespace Slic3r { namespace AABBTreeLines {
namespace detail {
template<typename ALineType, typename ATreeType, typename AVectorType>
struct IndexedLinesDistancer {
using LineType = ALineType;
using TreeType = ATreeType;
template<typename ALineType, typename ATreeType, typename AVectorType> struct IndexedLinesDistancer
{
using LineType = ALineType;
using TreeType = ATreeType;
using VectorType = AVectorType;
using ScalarType = typename VectorType::Scalar;
const std::vector<LineType> &lines;
const TreeType &tree;
const TreeType &tree;
const VectorType origin;
inline VectorType closest_point_to_origin(size_t primitive_index,
ScalarType &squared_distance) const {
inline VectorType closest_point_to_origin(size_t primitive_index, ScalarType &squared_distance) const
{
Vec<LineType::Dim, typename LineType::Scalar> nearest_point;
const LineType &line = lines[primitive_index];
const LineType &line = lines[primitive_index];
squared_distance = line_alg::distance_to_squared(line, origin.template cast<typename LineType::Scalar>(), &nearest_point);
return nearest_point.template cast<ScalarType>();
}
};
// !!! NOTE: algorithm expects the BoundingBoxes to be snug, no epsilon is allowed
template<typename LineType, typename TreeType, typename VectorType, int coordinate>
inline std::tuple<size_t, size_t> coordinate_aligned_ray_hit_count(size_t node_idx, const TreeType &tree, const VectorType &ray_origin)
{
static constexpr int other_coordinate = (coordinate + 1) % 2;
using Scalar = typename LineType::Scalar;
using Floating = typename std::conditional<std::is_floating_point<Scalar>::value, Scalar, double>::type;
const auto &node = tree.node(node_idx);
assert(node.is_valid());
if (node.is_leaf()) {
if (ray_origin[coordinate] > node.bbox.max()[coordinate]) {
return {1, 0};
} else if (ray_origin[coordinate] < node.bbox.min()[coordinate]) {
return {0, 1};
} else {
auto sizes = node.bbox.sizes();
Floating t = (ray_origin[other_coordinate] - node.bbox.min()[other_coordinate]) /
(sizes[other_coordinate] > 0 ? sizes[other_coordinate] : 1);
auto intersection = node.bbox.min()[coordinate] + t * sizes[coordinate];
if (ray_origin[coordinate] > intersection) {
return {1, 0};
} else if (ray_origin[coordinate] < intersection) {
return {0, 1};
} else { // ray origin is on boundary
return {1, 1};
}
}
} else {
size_t intersections_above = 0;
size_t intersections_below = 0;
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.min()[other_coordinate] <= ray_origin[other_coordinate] &&
node_left.bbox.max()[other_coordinate] > ray_origin[other_coordinate]) { // sharp inequality, beacuse we do not want to count point common to two lines more than once
auto [above, below] = coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, coordinate>(left_node_idx, tree,
ray_origin);
intersections_above += above;
intersections_below += below;
}
if (node_right.bbox.min()[other_coordinate] <= ray_origin[other_coordinate] &&
node_right.bbox.max()[other_coordinate] > ray_origin[other_coordinate]) {
auto [above, below] = coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, coordinate>(right_node_idx, tree,
ray_origin);
intersections_above += above;
intersections_below += below;
}
return {intersections_above, intersections_below};
}
}
} // namespace detail
// Build a balanced AABB Tree over a vector of lines, balancing the tree
// on centroids of the lines.
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
// during tree traversal.
template<typename LineType>
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
const std::vector<LineType> &lines,
//FIXME do we want to apply an epsilon?
const double eps = 0)
{
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType> &lines)
{
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
// using CoordType = typename TreeType::CoordType;
using VectorType = typename TreeType::VectorType;
using BoundingBox = typename TreeType::BoundingBox;
struct InputType {
size_t idx() const {
return m_idx;
}
const BoundingBox& bbox() const {
return m_bbox;
}
const VectorType& centroid() const {
return m_centroid;
}
struct InputType
{
size_t idx() const { return m_idx; }
const BoundingBox &bbox() const { return m_bbox; }
const VectorType &centroid() const { return m_centroid; }
size_t m_idx;
size_t m_idx;
BoundingBox m_bbox;
VectorType m_centroid;
VectorType m_centroid;
};
std::vector<InputType> input;
input.reserve(lines.size());
const VectorType veps(eps, eps);
for (size_t i = 0; i < lines.size(); ++i) {
const LineType &line = lines[i];
InputType n;
n.m_idx = i;
InputType n;
n.m_idx = i;
n.m_centroid = (line.a + line.b) * 0.5;
n.m_bbox = BoundingBox(line.a, line.a);
n.m_bbox = BoundingBox(line.a, line.a);
n.m_bbox.extend(line.b);
n.m_bbox.min() -= veps;
n.m_bbox.max() += veps;
input.emplace_back(n);
}
@ -112,7 +155,7 @@ template<typename LineType, typename TreeType, typename VectorType>
inline std::vector<size_t> all_lines_in_radius(const std::vector<LineType> &lines,
const TreeType &tree,
const VectorType &point,
typename VectorType::Scalar max_distance_squared)
typename VectorType::Scalar max_distance_squared)
{
auto distancer = detail::IndexedLinesDistancer<LineType, TreeType, VectorType>{lines, tree, point};
@ -123,6 +166,30 @@ inline std::vector<size_t> all_lines_in_radius(const std::vector<LineType> &line
return found_lines;
}
// return 1 if true, -1 if false, 0 if cannot be determined
template<typename LineType, typename TreeType, typename VectorType>
inline int point_outside_closed_contours(const std::vector<LineType> &lines, const TreeType &tree, const VectorType &point)
{
if (tree.empty()) { return 1; }
auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, 0>(0, tree, point);
std::cout << "hits_above: " << hits_above << " hits_below: " << hits_below << std::endl;
if (hits_above % 2 == 1 && hits_below % 2 == 1) {
return -1;
} else if (hits_above % 2 == 0 && hits_below % 2 == 0) {
return 1;
} else { // this should not happen with closed contours. lets check it in Y direction
auto [hits_above, hits_below] = detail::coordinate_aligned_ray_hit_count<LineType, TreeType, VectorType, 1>(0, tree, point);
if (hits_above % 2 == 1 && hits_below % 2 == 1) {
return -1;
} else if (hits_above % 2 == 0 && hits_below % 2 == 0) {
return 1;
} else { // both results were unclear
return 0;
}
}
}
template<typename LineType> class LinesDistancer
{
private:
@ -144,6 +211,9 @@ public:
LinesDistancer() = default;
// 1 true, -1 false, 0 cannot determine
int outside(const Vec<2, Scalar> &point) const { return point_outside_closed_contours(lines, tree, point); }
// negative sign means inside
std::tuple<Floating, size_t, Vec<2, Floating>> signed_distance_from_lines_extra(const Vec<2, Scalar> &point) const
{
@ -153,26 +223,9 @@ public:
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, nearest_line_index_out, nearest_point_out);
if (distance < 0) { return {std::numeric_limits<Floating>::infinity(), nearest_line_index_out, nearest_point_out}; }
distance = sqrt(distance);
const LineType &line = lines[nearest_line_index_out];
Vec<2, Floating> v1 = (line.b - line.a).template cast<Floating>();
Vec<2, Floating> v2 = (point - line.a).template cast<Floating>();
auto d1 = (v1.x() * v2.y()) - (v1.y() * v2.x());
LineType second_line = line;
if ((line.a.template cast<Floating>() - nearest_point_out).squaredNorm() < SCALED_EPSILON) {
second_line = lines[prev_idx_modulo(nearest_line_index_out, lines.size())];
} else {
second_line = lines[next_idx_modulo(nearest_line_index_out, lines.size())];
}
v1 = (second_line.b - second_line.a).template cast<Floating>();
v2 = (point - second_line.a).template cast<Floating>();
auto d2 = (v1.x() * v2.y()) - (v1.y() * v2.x());
auto d = abs(d1) > abs(d2) ? d1 : d2;
if (d > 0.0) { distance *= -1.0; }
distance = sqrt(distance);
distance *= outside(point);
return {distance, nearest_line_index_out, nearest_point_out};
}

View File

@ -7,6 +7,10 @@
#include "../ExtrusionEntity.hpp"
#include "../Layer.hpp"
#include "../Point.hpp"
#include "../SVG.hpp"
#include "../BoundingBox.hpp"
#include "../Polygon.hpp"
#include "../ClipperUtils.hpp"
#include <algorithm>
#include <cmath>
@ -62,7 +66,7 @@ public:
class CurvatureEstimator
{
static const size_t sliders_count = 3;
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{2.0},{4.0}, {8.0}};
SlidingWindowCurvatureAccumulator sliders[sliders_count] = {{2.0}, {4.0}, {8.0}};
public:
void add_point(float distance, float angle)
@ -101,6 +105,34 @@ public:
}
prev_layer_boundary = next_layer_boundary;
next_layer_boundary = AABBTreeLines::LinesDistancer<Linef>{std::move(layer_lines)};
#if 0 // EXPORT DEBUG FILES
Lines scaled_lines;
for (const Linef &lf : layer_lines) { scaled_lines.push_back({Point::new_scale(lf.a), Point::new_scale(lf.b)}); }
BoundingBox bb = get_extents(scaled_lines);
Points inside;
for (const Layer *layer : layers) {
if (layer == nullptr) continue;
auto in = to_points(to_polygons(offset_ex(layer->lslices, -scale_(0.4))));
inside.insert(inside.end(), in.begin(), in.end());
}
::Slic3r::SVG svg(debug_out_path(("path_jps" + std::to_string(rand() % 1000)).c_str()).c_str(), bb);
svg.draw(scaled_lines, "black", scale_(0.10));
for (Point p : inside) {
auto [distance, line_idx, nearest_point] = next_layer_boundary.signed_distance_from_lines_extra(unscaled(p));
if (distance > 0) {
svg.draw(p, "red", scale_(0.2));
svg.draw(Point::new_scale(nearest_point.x(), nearest_point.y()), "blue", scale_(0.2));
auto li = next_layer_boundary.get_line(line_idx);
Line ls{Point::new_scale(li.a), Point::new_scale(li.b)};
svg.draw(ls, "yellow", scale_(0.2));
}
}
#endif
}
std::vector<float> estimate_extrusion_quality(const ExtrusionPath &path)
@ -121,7 +153,7 @@ public:
double dist_from_prev_layer = prev_layer_boundary.signed_distance_from_lines(p.cast<double>()) + flow_width * 0.5f;
float default_dist_quality = 0.5f;
float default_dist_quality = 0.3f;
float distance_quality = 1.0f;
if (dist_from_prev_layer < min_malformation_dist) {
distance_quality = 1.0f;