Fix AABB tree query, add new fast query for point outside, which uses axis aligned rays
This commit is contained in:
parent
f4f1958cac
commit
11b03804dc
2 changed files with 144 additions and 59 deletions
|
@ -8,78 +8,121 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r { namespace AABBTreeLines {
|
||||||
|
|
||||||
namespace AABBTreeLines {
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<typename ALineType, typename ATreeType, typename AVectorType>
|
template<typename ALineType, typename ATreeType, typename AVectorType> struct IndexedLinesDistancer
|
||||||
struct IndexedLinesDistancer {
|
{
|
||||||
using LineType = ALineType;
|
using LineType = ALineType;
|
||||||
using TreeType = ATreeType;
|
using TreeType = ATreeType;
|
||||||
using VectorType = AVectorType;
|
using VectorType = AVectorType;
|
||||||
using ScalarType = typename VectorType::Scalar;
|
using ScalarType = typename VectorType::Scalar;
|
||||||
|
|
||||||
const std::vector<LineType> &lines;
|
const std::vector<LineType> &lines;
|
||||||
const TreeType &tree;
|
const TreeType &tree;
|
||||||
|
|
||||||
const VectorType origin;
|
const VectorType origin;
|
||||||
|
|
||||||
inline VectorType closest_point_to_origin(size_t primitive_index,
|
inline VectorType closest_point_to_origin(size_t primitive_index, ScalarType &squared_distance) const
|
||||||
ScalarType &squared_distance) const {
|
{
|
||||||
Vec<LineType::Dim, typename LineType::Scalar> nearest_point;
|
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);
|
squared_distance = line_alg::distance_to_squared(line, origin.template cast<typename LineType::Scalar>(), &nearest_point);
|
||||||
return nearest_point.template cast<ScalarType>();
|
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
|
// Build a balanced AABB Tree over a vector of lines, balancing the tree
|
||||||
// on centroids of the lines.
|
// on centroids of the lines.
|
||||||
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
|
// Epsilon is applied to the bounding boxes of the AABB Tree to cope with numeric inaccuracies
|
||||||
// during tree traversal.
|
// during tree traversal.
|
||||||
template<typename LineType>
|
template<typename LineType>
|
||||||
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(
|
inline AABBTreeIndirect::Tree<2, typename LineType::Scalar> build_aabb_tree_over_indexed_lines(const std::vector<LineType> &lines)
|
||||||
const std::vector<LineType> &lines,
|
{
|
||||||
//FIXME do we want to apply an epsilon?
|
|
||||||
const double eps = 0)
|
|
||||||
{
|
|
||||||
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
|
using TreeType = AABBTreeIndirect::Tree<2, typename LineType::Scalar>;
|
||||||
// using CoordType = typename TreeType::CoordType;
|
// using CoordType = typename TreeType::CoordType;
|
||||||
using VectorType = typename TreeType::VectorType;
|
using VectorType = typename TreeType::VectorType;
|
||||||
using BoundingBox = typename TreeType::BoundingBox;
|
using BoundingBox = typename TreeType::BoundingBox;
|
||||||
|
|
||||||
struct InputType {
|
struct InputType
|
||||||
size_t idx() const {
|
{
|
||||||
return m_idx;
|
size_t idx() const { return m_idx; }
|
||||||
}
|
const BoundingBox &bbox() const { return m_bbox; }
|
||||||
const BoundingBox& bbox() const {
|
const VectorType ¢roid() const { return m_centroid; }
|
||||||
return m_bbox;
|
|
||||||
}
|
|
||||||
const VectorType& centroid() const {
|
|
||||||
return m_centroid;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t m_idx;
|
size_t m_idx;
|
||||||
BoundingBox m_bbox;
|
BoundingBox m_bbox;
|
||||||
VectorType m_centroid;
|
VectorType m_centroid;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<InputType> input;
|
std::vector<InputType> input;
|
||||||
input.reserve(lines.size());
|
input.reserve(lines.size());
|
||||||
const VectorType veps(eps, eps);
|
|
||||||
for (size_t i = 0; i < lines.size(); ++i) {
|
for (size_t i = 0; i < lines.size(); ++i) {
|
||||||
const LineType &line = lines[i];
|
const LineType &line = lines[i];
|
||||||
InputType n;
|
InputType n;
|
||||||
n.m_idx = i;
|
n.m_idx = i;
|
||||||
n.m_centroid = (line.a + line.b) * 0.5;
|
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.extend(line.b);
|
||||||
n.m_bbox.min() -= veps;
|
|
||||||
n.m_bbox.max() += veps;
|
|
||||||
input.emplace_back(n);
|
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,
|
inline std::vector<size_t> all_lines_in_radius(const std::vector<LineType> &lines,
|
||||||
const TreeType &tree,
|
const TreeType &tree,
|
||||||
const VectorType &point,
|
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};
|
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 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
|
template<typename LineType> class LinesDistancer
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -144,6 +211,9 @@ public:
|
||||||
|
|
||||||
LinesDistancer() = default;
|
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
|
// negative sign means inside
|
||||||
std::tuple<Floating, size_t, Vec<2, Floating>> signed_distance_from_lines_extra(const Vec<2, Scalar> &point) const
|
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);
|
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}; }
|
if (distance < 0) { return {std::numeric_limits<Floating>::infinity(), nearest_line_index_out, nearest_point_out}; }
|
||||||
distance = sqrt(distance);
|
distance = sqrt(distance);
|
||||||
const LineType &line = lines[nearest_line_index_out];
|
distance *= outside(point);
|
||||||
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; }
|
|
||||||
|
|
||||||
return {distance, nearest_line_index_out, nearest_point_out};
|
return {distance, nearest_line_index_out, nearest_point_out};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
#include "../ExtrusionEntity.hpp"
|
#include "../ExtrusionEntity.hpp"
|
||||||
#include "../Layer.hpp"
|
#include "../Layer.hpp"
|
||||||
#include "../Point.hpp"
|
#include "../Point.hpp"
|
||||||
|
#include "../SVG.hpp"
|
||||||
|
#include "../BoundingBox.hpp"
|
||||||
|
#include "../Polygon.hpp"
|
||||||
|
#include "../ClipperUtils.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -62,7 +66,7 @@ public:
|
||||||
class CurvatureEstimator
|
class CurvatureEstimator
|
||||||
{
|
{
|
||||||
static const size_t sliders_count = 3;
|
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:
|
public:
|
||||||
void add_point(float distance, float angle)
|
void add_point(float distance, float angle)
|
||||||
|
@ -101,6 +105,34 @@ public:
|
||||||
}
|
}
|
||||||
prev_layer_boundary = next_layer_boundary;
|
prev_layer_boundary = next_layer_boundary;
|
||||||
next_layer_boundary = AABBTreeLines::LinesDistancer<Linef>{std::move(layer_lines)};
|
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)
|
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;
|
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;
|
float distance_quality = 1.0f;
|
||||||
if (dist_from_prev_layer < min_malformation_dist) {
|
if (dist_from_prev_layer < min_malformation_dist) {
|
||||||
distance_quality = 1.0f;
|
distance_quality = 1.0f;
|
||||||
|
|
Loading…
Reference in a new issue