NEW: enable lightning infill pattern for model and tree support

Change-Id: I6e2cbfdd30f8d222f88301ed0c8cc89e21cfdc24
(cherry picked from commit ddfee7c069cfc42685be509d48b8c609e1dc0cfc)
This commit is contained in:
xiang.zeng 2022-07-06 12:16:37 +08:00 committed by Lane.Wei
parent f331d5998e
commit 9cf95696a4
22 changed files with 1014 additions and 321 deletions

View file

@ -3,12 +3,16 @@
#include "Layer.hpp" //The class we're implementing.
#include <iterator> // advance
#include "DistanceField.hpp"
#include "TreeNode.hpp"
#include "../../ClipperUtils.hpp"
#include "../../Geometry.hpp"
#include "Utils.hpp"
#include <tbb/parallel_for.h>
#include <tbb/blocked_range2d.h>
#include <mutex>
namespace Slic3r::FillLightning {
@ -23,10 +27,15 @@ Point GroundingLocation::p() const
return tree_node ? tree_node->getLocation() : *boundary_location;
}
void Layer::fillLocator(SparseNodeGrid &tree_node_locator)
inline static Point to_grid_point(const Point &point, const BoundingBox &bbox)
{
std::function<void(NodeSPtr)> add_node_to_locator_func = [&tree_node_locator](NodeSPtr node) {
tree_node_locator.insert(std::make_pair(Point(node->getLocation().x() / locator_cell_size, node->getLocation().y() / locator_cell_size), node));
return (point - bbox.min) / locator_cell_size;
}
void Layer::fillLocator(SparseNodeGrid &tree_node_locator, const BoundingBox& current_outlines_bbox)
{
std::function<void(NodeSPtr)> add_node_to_locator_func = [&tree_node_locator, &current_outlines_bbox](const NodeSPtr &node) {
tree_node_locator.insert(std::make_pair(to_grid_point(node->getLocation(), current_outlines_bbox), node));
};
for (auto& tree : tree_roots)
tree->visitNodes(add_node_to_locator_func);
@ -36,38 +45,47 @@ void Layer::generateNewTrees
(
const Polygons& current_overhang,
const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outlines_locator,
const coord_t supporting_radius,
const coord_t wall_supporting_radius
)
{
DistanceField distance_field(supporting_radius, current_outlines, current_overhang);
DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang);
SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator);
fillLocator(tree_node_locator, current_outlines_bbox);
// Until no more points need to be added to support all:
// Determine next point from tree/outline areas via distance-field
Point unsupported_location;
while (distance_field.tryGetNextPoint(&unsupported_location)) {
size_t unsupported_cell_idx = 0;
Point unsupported_location;
while (distance_field.tryGetNextPoint(&unsupported_location, &unsupported_cell_idx, unsupported_cell_idx)) {
GroundingLocation grounding_loc = getBestGroundingLocation(
unsupported_location, current_outlines, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);
unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);
NodeSPtr new_parent;
NodeSPtr new_child;
this->attach(unsupported_location, grounding_loc, new_child, new_parent);
tree_node_locator.insert(std::make_pair(Point(new_child->getLocation().x() / locator_cell_size, new_child->getLocation().y() / locator_cell_size), new_child));
tree_node_locator.insert(std::make_pair(to_grid_point(new_child->getLocation(), current_outlines_bbox), new_child));
if (new_parent)
tree_node_locator.insert(std::make_pair(Point(new_parent->getLocation().x() / locator_cell_size, new_parent->getLocation().y() / locator_cell_size), new_parent));
tree_node_locator.insert(std::make_pair(to_grid_point(new_parent->getLocation(), current_outlines_bbox), new_parent));
// update distance field
distance_field.update(grounding_loc.p(), unsupported_location);
}
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
{
static int iRun = 0;
export_to_svg(debug_out_path("FillLightning-TreeNodes-%d.svg", iRun++), current_outlines, this->tree_roots);
}
#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */
}
static bool polygonCollidesWithLineSegment(const Point from, const Point to, const EdgeGrid::Grid &loc_to_line)
static bool polygonCollidesWithLineSegment(const Point &from, const Point &to, const EdgeGrid::Grid &loc_to_line)
{
struct Visitor {
explicit Visitor(const EdgeGrid::Grid &grid) : grid(grid) {}
explicit Visitor(const EdgeGrid::Grid &grid, const Line &line) : grid(grid), line(line) {}
bool operator()(coord_t iy, coord_t ix) {
// Called with a row and colum of the grid cell, which is intersected by a line.
@ -87,7 +105,7 @@ static bool polygonCollidesWithLineSegment(const Point from, const Point to, con
const EdgeGrid::Grid& grid;
Line line;
bool intersect = false;
} visitor(loc_to_line);
} visitor(loc_to_line, {from, to});
loc_to_line.visit_cells_intersecting_line(from, to, visitor);
return visitor.intersect;
@ -97,6 +115,7 @@ GroundingLocation Layer::getBestGroundingLocation
(
const Point& unsupported_location,
const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius,
const coord_t wall_supporting_radius,
@ -112,9 +131,10 @@ GroundingLocation Layer::getBestGroundingLocation
if (contour.size() > 2) {
Point prev = contour.points.back();
for (const Point &p2 : contour.points) {
if (double d = Line::distance_to_squared(unsupported_location, prev, p2); d < d2) {
Point closest_point;
if (double d = line_alg::distance_to_squared(Line{prev, p2}, unsupported_location, &closest_point); d < d2) {
d2 = d;
node_location = Geometry::foot_pt({ prev, p2 }, unsupported_location).cast<coord_t>();
node_location = closest_point;
}
prev = p2;
}
@ -123,30 +143,52 @@ GroundingLocation Layer::getBestGroundingLocation
const auto within_dist = coord_t((node_location - unsupported_location).cast<double>().norm());
NodeSPtr sub_tree{ nullptr };
coord_t current_dist = getWeightedDistance(node_location, unsupported_location);
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 /= locator_cell_size;
region.max /= locator_cell_size;
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();
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;
region.min = to_grid_point(region.min, current_outlines_bbox);
region.max = to_grid_point(region.max, current_outlines_bbox);
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()), [&current_dist, current_dist_copy = current_dist, &current_dist_mutex, &sub_tree, &current_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)) {
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 ?
@ -176,6 +218,7 @@ void Layer::reconnectRoots
(
std::vector<NodeSPtr>& to_be_reconnected_tree_roots,
const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius,
const coord_t wall_supporting_radius
@ -184,10 +227,10 @@ void Layer::reconnectRoots
constexpr coord_t tree_connecting_ignore_offset = 100;
SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator);
fillLocator(tree_node_locator, current_outlines_bbox);
const coord_t within_max_dist = outline_locator.resolution() * 2;
for (auto root_ptr : to_be_reconnected_tree_roots)
for (const auto &root_ptr : to_be_reconnected_tree_roots)
{
auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr);
@ -203,7 +246,7 @@ void Layer::reconnectRoots
root_ptr->addChild(new_root);
new_root->reroot();
tree_node_locator.insert(std::make_pair(Point(new_root->getLocation().x() / locator_cell_size, new_root->getLocation().y() / locator_cell_size), new_root));
tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root));
*old_root_it = std::move(new_root); // replace old root with new root
continue;
@ -217,6 +260,7 @@ void Layer::reconnectRoots
(
root_ptr->getLocation(),
current_outlines,
current_outlines_bbox,
outline_locator,
supporting_radius,
tree_connecting_ignore_width,
@ -233,7 +277,7 @@ void Layer::reconnectRoots
attach_ptr->reroot();
new_root->addChild(attach_ptr);
tree_node_locator.insert(std::make_pair(new_root->getLocation(), new_root));
tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root));
*old_root_it = std::move(new_root); // replace old root with new root
}
@ -256,15 +300,26 @@ void Layer::reconnectRoots
}
}
/*
* Implementation assumes moving inside, but moving outside should just as well be possible.
#if 0
/*!
* Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance.
* Given a \p distance more than zero, the point will end up inside, and conversely outside.
* When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned.
* When the point is in/outside by less than \p distance, \p from is moved to the correct place.
* Implementation assumes moving inside, but moving outside should just as well be possible.
*
* \param polygons The polygons onto which to move the point
* \param from[in,out] The point to move.
* \param distance The distance by which to move the point.
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \return The index to the polygon onto which we have moved the point.
*/
static unsigned int moveInside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2)
{
Point ret = from;
int64_t bestDist2 = std::numeric_limits<int64_t>::max();
unsigned int bestPoly = static_cast<unsigned int>(-1);
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
Point ret = from;
int64_t bestDist2 = std::numeric_limits<int64_t>::max();
auto bestPoly = static_cast<unsigned int>(-1);
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{
const Polygon &poly = polygons[poly_idx];
@ -333,7 +388,7 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan
else
{ // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
projected_p_beyond_prev_segment = false;
Point x = a + ab * dot_prod / ab_length2;
Point x = (a.cast<int64_t>() + ab.cast<int64_t>() * dot_prod / ab_length2).cast<coord_t>();
int64_t dist2 = (p - x).cast<int64_t>().squaredNorm();
if (dist2 < bestDist2)
@ -373,38 +428,18 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan
}
return static_cast<unsigned int>(-1);
}
#endif
// Returns 'added someting'.
Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const
Polylines Layer::convertToLines(const Polygons& limit_to_outline, const coord_t line_overlap) const
{
if (tree_roots.empty())
return {};
Polygons result_lines;
for (const auto& tree : tree_roots) {
// If even the furthest location in the tree is inside the polygon, the entire tree must be inside of the polygon.
// (Don't take the root as that may be on the edge and cause rounding errors to register as 'outside'.)
constexpr coord_t epsilon = 5;
Point should_be_inside = tree->getLocation();
moveInside(limit_to_outline, should_be_inside, epsilon, epsilon * epsilon);
if (inside(limit_to_outline, should_be_inside))
tree->convertToPolylines(result_lines, line_width);
}
Polylines result_lines;
for (const auto &tree : tree_roots)
tree->convertToPolylines(result_lines, line_overlap);
// TODO: allow for polylines!
Polylines split_lines;
for (Polygon &line : result_lines) {
if (line.size() <= 1)
continue;
Point last = line[0];
for (size_t point_idx = 1; point_idx < line.size(); point_idx++) {
Point here = line[point_idx];
split_lines.push_back({ last, here });
last = here;
}
}
return split_lines;
return intersection_pl(result_lines, limit_to_outline);
}
} // namespace Slic3r::Lightning