Merge branch 'lh_lightning_infill_fix'
This commit is contained in:
commit
fe16fb41dd
@ -5,6 +5,8 @@
|
||||
#include "../FillRectilinear.hpp"
|
||||
#include "../../ClipperUtils.hpp"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
namespace Slic3r::FillLightning
|
||||
{
|
||||
|
||||
@ -18,7 +20,13 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl
|
||||
m_supporting_radius2 = Slic3r::sqr(int64_t(radius));
|
||||
// Sample source polygons with a regular grid sampling pattern.
|
||||
for (const ExPolygon &expoly : union_ex(current_overhang)) {
|
||||
for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) {
|
||||
const Points sampled_points = sample_grid_pattern(expoly, m_cell_size);
|
||||
const size_t unsupported_points_prev_size = m_unsupported_points.size();
|
||||
m_unsupported_points.resize(unsupported_points_prev_size + sampled_points.size());
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(0, sampled_points.size()), [&self = *this, &expoly = std::as_const(expoly), &sampled_points = std::as_const(sampled_points), &unsupported_points_prev_size = std::as_const(unsupported_points_prev_size)](const tbb::blocked_range<size_t> &range) -> void {
|
||||
for (size_t sp_idx = range.begin(); sp_idx < range.end(); ++sp_idx) {
|
||||
const Point &sp = sampled_points[sp_idx];
|
||||
// Find a squared distance to the source expolygon boundary.
|
||||
double d2 = std::numeric_limits<double>::max();
|
||||
for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) {
|
||||
@ -26,25 +34,28 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl
|
||||
if (contour.size() > 2) {
|
||||
Point prev = contour.points.back();
|
||||
for (const Point &p2 : contour.points) {
|
||||
d2 = std::min(d2, Line::distance_to_squared(p, prev, p2));
|
||||
d2 = std::min(d2, Line::distance_to_squared(sp, prev, p2));
|
||||
prev = p2;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_unsupported_points.emplace_back(p, sqrt(d2));
|
||||
assert(m_unsupported_points_bbox.contains(p));
|
||||
self.m_unsupported_points[unsupported_points_prev_size + sp_idx] = {sp, coord_t(std::sqrt(d2))};
|
||||
assert(self.m_unsupported_points_bbox.contains(sp));
|
||||
}
|
||||
}); // end of parallel_for
|
||||
}
|
||||
m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) {
|
||||
std::stable_sort(m_unsupported_points.begin(), m_unsupported_points.end(), [&radius](const UnsupportedCell &a, const UnsupportedCell &b) {
|
||||
constexpr coord_t prime_for_hash = 191;
|
||||
return std::abs(b.dist_to_boundary - a.dist_to_boundary) > radius ?
|
||||
a.dist_to_boundary < b.dist_to_boundary :
|
||||
(PointHash{}(a.loc) % prime_for_hash) < (PointHash{}(b.loc) % prime_for_hash);
|
||||
});
|
||||
for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) {
|
||||
UnsupportedCell& cell = *it;
|
||||
m_unsupported_points_grid.emplace(this->to_grid_point(cell.loc), it);
|
||||
}
|
||||
|
||||
m_unsupported_points_erased.resize(m_unsupported_points.size());
|
||||
std::fill(m_unsupported_points_erased.begin(), m_unsupported_points_erased.end(), false);
|
||||
|
||||
m_unsupported_points_grid.initialize(m_unsupported_points, [&self = std::as_const(*this)](const Point &p) -> Point { return self.to_grid_point(p); });
|
||||
|
||||
// Because the distance between two points is at least one axis equal to m_cell_size, every cell
|
||||
// in m_unsupported_points_grid contains exactly one point.
|
||||
assert(m_unsupported_points.size() == m_unsupported_points_grid.size());
|
||||
@ -96,12 +107,11 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf)
|
||||
}
|
||||
// Inside a circle at the end of the new leaf, or inside a rotated rectangle.
|
||||
// Remove unsupported leafs at this grid location.
|
||||
if (auto it = m_unsupported_points_grid.find(grid_addr); it != m_unsupported_points_grid.end()) {
|
||||
std::list<UnsupportedCell>::iterator& list_it = it->second;
|
||||
UnsupportedCell& cell = *list_it;
|
||||
if (const size_t cell_idx = m_unsupported_points_grid.find_cell_idx(grid_addr); cell_idx != std::numeric_limits<size_t>::max()) {
|
||||
const UnsupportedCell &cell = m_unsupported_points[cell_idx];
|
||||
if ((cell.loc - added_leaf).cast<int64_t>().squaredNorm() <= m_supporting_radius2) {
|
||||
m_unsupported_points.erase(list_it);
|
||||
m_unsupported_points_grid.erase(it);
|
||||
m_unsupported_points_erased[cell_idx] = true;
|
||||
m_unsupported_points_grid.mark_erased(grid_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,12 +38,18 @@ public:
|
||||
* \return ``true`` if successful, or ``false`` if there are no more points
|
||||
* to consider.
|
||||
*/
|
||||
bool tryGetNextPoint(Point* p) const {
|
||||
if (m_unsupported_points.empty())
|
||||
return false;
|
||||
*p = m_unsupported_points.front().loc;
|
||||
bool tryGetNextPoint(Point *out_unsupported_location, size_t *out_unsupported_cell_idx, const size_t start_idx = 0) const
|
||||
{
|
||||
for (size_t point_idx = start_idx; point_idx < m_unsupported_points.size(); ++point_idx) {
|
||||
if (!m_unsupported_points_erased[point_idx]) {
|
||||
*out_unsupported_cell_idx = point_idx;
|
||||
*out_unsupported_location = m_unsupported_points[point_idx].loc;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Update the distance field with a newly added branch.
|
||||
@ -77,7 +83,6 @@ protected:
|
||||
*/
|
||||
struct UnsupportedCell
|
||||
{
|
||||
UnsupportedCell(const Point &loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {}
|
||||
// The position of the center of this cell.
|
||||
Point loc;
|
||||
// How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area.
|
||||
@ -87,7 +92,8 @@ protected:
|
||||
/*!
|
||||
* Cells which still need to be supported at some point.
|
||||
*/
|
||||
std::list<UnsupportedCell> m_unsupported_points;
|
||||
std::vector<UnsupportedCell> m_unsupported_points;
|
||||
std::vector<bool> m_unsupported_points_erased;
|
||||
|
||||
/*!
|
||||
* BoundingBox of all points in m_unsupported_points. Used for mapping of sign integer numbers to positive integer numbers.
|
||||
@ -98,7 +104,84 @@ protected:
|
||||
* Links the unsupported points to a grid point, so that we can quickly look
|
||||
* up the cell belonging to a certain position in the grid.
|
||||
*/
|
||||
std::unordered_map<Point, std::list<UnsupportedCell>::iterator, PointHash> m_unsupported_points_grid;
|
||||
|
||||
class UnsupportedPointsGrid
|
||||
{
|
||||
public:
|
||||
UnsupportedPointsGrid() = default;
|
||||
void initialize(const std::vector<UnsupportedCell> &unsupported_points, const std::function<Point(const Point &)> &map_cell_to_grid)
|
||||
{
|
||||
if (unsupported_points.empty())
|
||||
return;
|
||||
|
||||
BoundingBox unsupported_points_bbox;
|
||||
for (const UnsupportedCell &cell : unsupported_points)
|
||||
unsupported_points_bbox.merge(cell.loc);
|
||||
|
||||
m_size = unsupported_points.size();
|
||||
m_grid_range = BoundingBox(map_cell_to_grid(unsupported_points_bbox.min), map_cell_to_grid(unsupported_points_bbox.max));
|
||||
m_grid_size = m_grid_range.size() + Point::Ones();
|
||||
|
||||
m_data.assign(m_grid_size.y() * m_grid_size.x(), std::numeric_limits<size_t>::max());
|
||||
m_data_erased.assign(m_grid_size.y() * m_grid_size.x(), true);
|
||||
|
||||
for (size_t cell_idx = 0; cell_idx < unsupported_points.size(); ++cell_idx) {
|
||||
const size_t flat_idx = map_to_flat_array(map_cell_to_grid(unsupported_points[cell_idx].loc));
|
||||
assert(m_data[flat_idx] == std::numeric_limits<size_t>::max());
|
||||
m_data[flat_idx] = cell_idx;
|
||||
m_data_erased[flat_idx] = false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
size_t find_cell_idx(const Point &grid_addr)
|
||||
{
|
||||
if (!m_grid_range.contains(grid_addr))
|
||||
return std::numeric_limits<size_t>::max();
|
||||
|
||||
if (const size_t flat_idx = map_to_flat_array(grid_addr); !m_data_erased[flat_idx]) {
|
||||
assert(m_data[flat_idx] != std::numeric_limits<size_t>::max());
|
||||
return m_data[flat_idx];
|
||||
}
|
||||
|
||||
return std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
void mark_erased(const Point &grid_addr)
|
||||
{
|
||||
assert(m_grid_range.contains(grid_addr));
|
||||
if (!m_grid_range.contains(grid_addr))
|
||||
return;
|
||||
|
||||
const size_t flat_idx = map_to_flat_array(grid_addr);
|
||||
assert(!m_data_erased[flat_idx] && m_data[flat_idx] != std::numeric_limits<size_t>::max());
|
||||
assert(m_size != 0);
|
||||
|
||||
m_data_erased[flat_idx] = true;
|
||||
--m_size;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t m_size = 0;
|
||||
|
||||
BoundingBox m_grid_range;
|
||||
Point m_grid_size;
|
||||
|
||||
std::vector<size_t> m_data;
|
||||
std::vector<bool> m_data_erased;
|
||||
|
||||
inline size_t map_to_flat_array(const Point &loc) const
|
||||
{
|
||||
const Point offset_loc = loc - m_grid_range.min;
|
||||
const size_t flat_idx = m_grid_size.x() * offset_loc.y() + offset_loc.x();
|
||||
assert(offset_loc.x() >= 0 && offset_loc.y() >= 0);
|
||||
assert(flat_idx < m_grid_size.y() * m_grid_size.x());
|
||||
return flat_idx;
|
||||
}
|
||||
};
|
||||
|
||||
UnsupportedPointsGrid m_unsupported_points_grid;
|
||||
|
||||
/*!
|
||||
* Maps the point to the grid coordinates.
|
||||
|
@ -125,6 +125,8 @@ void Generator::generateTrees(const PrintObject &print_object, const std::functi
|
||||
if (const BoundingBox &outlines_locator_bbox = outlines_locator.bbox(); outlines_locator_bbox.defined)
|
||||
below_outlines_bbox.merge(outlines_locator_bbox);
|
||||
|
||||
below_outlines_bbox.merge(get_extents(current_lightning_layer.tree_roots).inflated(SCALED_EPSILON));
|
||||
|
||||
outlines_locator.set_bbox(below_outlines_bbox);
|
||||
outlines_locator.create(below_outlines, locator_cell_size);
|
||||
|
||||
|
@ -10,6 +10,10 @@
|
||||
#include "../../Geometry.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/blocked_range2d.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace Slic3r::FillLightning {
|
||||
|
||||
coord_t Layer::getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location)
|
||||
@ -56,8 +60,9 @@ void Layer::generateNewTrees
|
||||
|
||||
// Until no more points need to be added to support all:
|
||||
// Determine next point from tree/outline areas via distance-field
|
||||
size_t unsupported_cell_idx = 0;
|
||||
Point unsupported_location;
|
||||
while (distance_field.tryGetNextPoint(&unsupported_location)) {
|
||||
while (distance_field.tryGetNextPoint(&unsupported_location, &unsupported_cell_idx, unsupported_cell_idx)) {
|
||||
throw_on_cancel_callback();
|
||||
GroundingLocation grounding_loc = getBestGroundingLocation(
|
||||
unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);
|
||||
@ -141,31 +146,53 @@ GroundingLocation Layer::getBestGroundingLocation
|
||||
|
||||
const auto within_dist = coord_t((node_location - unsupported_location).cast<double>().norm());
|
||||
|
||||
NodeSPtr sub_tree{ nullptr };
|
||||
NodeSPtr sub_tree{nullptr};
|
||||
coord_t current_dist = getWeightedDistance(node_location, unsupported_location);
|
||||
if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines.
|
||||
const coord_t search_radius = std::min(current_dist, within_dist);
|
||||
BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size));
|
||||
region.min = to_grid_point(region.min, current_outlines_bbox);
|
||||
region.max = to_grid_point(region.max, current_outlines_bbox);
|
||||
Point grid_addr;
|
||||
for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y())
|
||||
for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) {
|
||||
auto it_range = tree_node_locator.equal_range(grid_addr);
|
||||
for (auto it = it_range.first; it != it_range.second; ++ it) {
|
||||
auto candidate_sub_tree = it->second.lock();
|
||||
|
||||
Point current_dist_grid_addr{std::numeric_limits<coord_t>::lowest(), std::numeric_limits<coord_t>::lowest()};
|
||||
std::mutex current_dist_mutex;
|
||||
tbb::parallel_for(tbb::blocked_range2d<coord_t>(region.min.y(), region.max.y(), region.min.x(), region.max.x()), [¤t_dist, current_dist_copy = current_dist, ¤t_dist_mutex, &sub_tree, ¤t_dist_grid_addr, &exclude_tree = std::as_const(exclude_tree), &outline_locator = std::as_const(outline_locator), &supporting_radius = std::as_const(supporting_radius), &tree_node_locator = std::as_const(tree_node_locator), &unsupported_location = std::as_const(unsupported_location)](const tbb::blocked_range2d<coord_t> &range) -> void {
|
||||
for (coord_t grid_addr_y = range.rows().begin(); grid_addr_y < range.rows().end(); ++grid_addr_y)
|
||||
for (coord_t grid_addr_x = range.cols().begin(); grid_addr_x < range.cols().end(); ++grid_addr_x) {
|
||||
const Point local_grid_addr{grid_addr_x, grid_addr_y};
|
||||
NodeSPtr local_sub_tree{nullptr};
|
||||
coord_t local_current_dist = current_dist_copy;
|
||||
const auto it_range = tree_node_locator.equal_range(local_grid_addr);
|
||||
for (auto it = it_range.first; it != it_range.second; ++it) {
|
||||
const NodeSPtr candidate_sub_tree = it->second.lock();
|
||||
if ((candidate_sub_tree && candidate_sub_tree != exclude_tree) &&
|
||||
!(exclude_tree && exclude_tree->hasOffspring(candidate_sub_tree)) &&
|
||||
!polygonCollidesWithLineSegment(unsupported_location, candidate_sub_tree->getLocation(), outline_locator)) {
|
||||
const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius);
|
||||
if (candidate_dist < current_dist) {
|
||||
current_dist = candidate_dist;
|
||||
sub_tree = candidate_sub_tree;
|
||||
if (const coord_t candidate_dist = candidate_sub_tree->getWeightedDistance(unsupported_location, supporting_radius); candidate_dist < local_current_dist) {
|
||||
local_current_dist = candidate_dist;
|
||||
local_sub_tree = candidate_sub_tree;
|
||||
}
|
||||
}
|
||||
}
|
||||
// To always get the same result in a parallel version as in a non-parallel version,
|
||||
// we need to preserve that for the same current_dist, we select the same sub_tree
|
||||
// as in the non-parallel version. For this purpose, inside the variable
|
||||
// current_dist_grid_addr is stored from with 2D grid position assigned sub_tree comes.
|
||||
// And when there are two sub_tree with the same current_dist, one which will be found
|
||||
// the first in the non-parallel version is selected.
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(current_dist_mutex);
|
||||
if (local_current_dist < current_dist ||
|
||||
(local_current_dist == current_dist && (grid_addr_y < current_dist_grid_addr.y() ||
|
||||
(grid_addr_y == current_dist_grid_addr.y() && grid_addr_x < current_dist_grid_addr.x())))) {
|
||||
current_dist = local_current_dist;
|
||||
sub_tree = local_sub_tree;
|
||||
current_dist_grid_addr = local_grid_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}); // end of parallel_for
|
||||
}
|
||||
|
||||
return ! sub_tree ?
|
||||
GroundingLocation{ nullptr, node_location } :
|
||||
|
@ -269,6 +269,9 @@ protected:
|
||||
|
||||
std::optional<Point> m_last_grounding_location; //<! The last known grounding location, see 'getLastGroundingLocation()'.
|
||||
|
||||
friend BoundingBox get_extents(const NodeSPtr &root_node);
|
||||
friend BoundingBox get_extents(const std::vector<NodeSPtr> &tree_roots);
|
||||
|
||||
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
|
||||
friend void export_to_svg(const NodeSPtr &root_node, Slic3r::SVG &svg);
|
||||
friend void export_to_svg(const std::string &path, const Polygons &contour, const std::vector<NodeSPtr> &root_nodes);
|
||||
@ -278,6 +281,23 @@ protected:
|
||||
bool inside(const Polygons &polygons, const Point &p);
|
||||
bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, coord_t within_max_dist);
|
||||
|
||||
inline BoundingBox get_extents(const NodeSPtr &root_node)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const NodeSPtr &children : root_node->m_children)
|
||||
bbox.merge(get_extents(children));
|
||||
bbox.merge(root_node->getLocation());
|
||||
return bbox;
|
||||
}
|
||||
|
||||
inline BoundingBox get_extents(const std::vector<NodeSPtr> &tree_roots)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
for (const NodeSPtr &root_node : tree_roots)
|
||||
bbox.merge(get_extents(root_node));
|
||||
return bbox;
|
||||
}
|
||||
|
||||
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
|
||||
void export_to_svg(const NodeSPtr &root_node, SVG &svg);
|
||||
void export_to_svg(const std::string &path, const Polygons &contour, const std::vector<NodeSPtr> &root_nodes);
|
||||
|
Loading…
Reference in New Issue
Block a user