mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-09-26 18:13:15 +08:00
Merge branch 'lh_long_slicing'
This commit is contained in:
commit
1b36955ca2
@ -18,8 +18,39 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r::Algorithm {
|
||||||
namespace Algorithm {
|
|
||||||
|
bool is_first_path_touching_second_path(const AABBTreeLines::LinesDistancer<Line> &first_distancer,
|
||||||
|
const AABBTreeLines::LinesDistancer<Line> &second_distancer,
|
||||||
|
const BoundingBox &second_distancer_bbox,
|
||||||
|
const double touch_distance_threshold)
|
||||||
|
{
|
||||||
|
for (const Line &line : first_distancer.get_lines()) {
|
||||||
|
if (bbox_point_distance(second_distancer_bbox, line.a) < touch_distance_threshold && second_distancer.distance_from_lines<false>(line.a) < touch_distance_threshold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Point first_distancer_last_pt = first_distancer.get_lines().back().b;
|
||||||
|
if (bbox_point_distance(second_distancer_bbox, first_distancer_last_pt) && second_distancer.distance_from_lines<false>(first_distancer_last_pt) < touch_distance_threshold) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool are_paths_touching(const AABBTreeLines::LinesDistancer<Line> &first_distancer, const BoundingBox &first_distancer_bbox,
|
||||||
|
const AABBTreeLines::LinesDistancer<Line> &second_distancer, const BoundingBox &second_distancer_bbox,
|
||||||
|
const double touch_distance_threshold)
|
||||||
|
{
|
||||||
|
if (is_first_path_touching_second_path(first_distancer, second_distancer, second_distancer_bbox, touch_distance_threshold)) {
|
||||||
|
return true;
|
||||||
|
} else if (is_first_path_touching_second_path(second_distancer, first_distancer, first_distancer_bbox, touch_distance_threshold)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//Sorts the paths such that all paths between begin and last_seed are printed first, in some order. The rest of the paths is sorted
|
//Sorts the paths such that all paths between begin and last_seed are printed first, in some order. The rest of the paths is sorted
|
||||||
// such that the paths that are touching some of the already printed are printed first, sorted secondary by the distance to the last point of the last
|
// such that the paths that are touching some of the already printed are printed first, sorted secondary by the distance to the last point of the last
|
||||||
@ -28,44 +59,34 @@ namespace Algorithm {
|
|||||||
// to the second, then they touch.
|
// to the second, then they touch.
|
||||||
// convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order.
|
// convert_to_lines is a lambda that should accept the path as argument and return it as Lines vector, in correct order.
|
||||||
template<typename RandomAccessIterator, typename ToLines>
|
template<typename RandomAccessIterator, typename ToLines>
|
||||||
void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, double touch_limit_distance, ToLines convert_to_lines)
|
void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point start, const double touch_distance_threshold, ToLines convert_to_lines)
|
||||||
{
|
{
|
||||||
size_t paths_count = std::distance(begin, end);
|
const size_t paths_count = std::distance(begin, end);
|
||||||
if (paths_count <= 1)
|
if (paths_count <= 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto paths_touch = [touch_limit_distance](const AABBTreeLines::LinesDistancer<Line> &left,
|
|
||||||
const AABBTreeLines::LinesDistancer<Line> &right) {
|
|
||||||
for (const Line &l : left.get_lines()) {
|
|
||||||
if (right.distance_from_lines<false>(l.a) < touch_limit_distance) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (right.distance_from_lines<false>(left.get_lines().back().b) < touch_limit_distance) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const Line &l : right.get_lines()) {
|
|
||||||
if (left.distance_from_lines<false>(l.a) < touch_limit_distance) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (left.distance_from_lines<false>(right.get_lines().back().b) < touch_limit_distance) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<AABBTreeLines::LinesDistancer<Line>> distancers(paths_count);
|
std::vector<AABBTreeLines::LinesDistancer<Line>> distancers(paths_count);
|
||||||
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
|
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
|
||||||
distancers[path_idx] = AABBTreeLines::LinesDistancer<Line>{convert_to_lines(*std::next(begin, path_idx))};
|
distancers[path_idx] = AABBTreeLines::LinesDistancer<Line>{convert_to_lines(*std::next(begin, path_idx))};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundingBoxes bboxes;
|
||||||
|
bboxes.reserve(paths_count);
|
||||||
|
for (auto tp_it = begin; tp_it != end; ++tp_it) {
|
||||||
|
bboxes.emplace_back(tp_it->bounding_box());
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::unordered_set<size_t>> dependencies(paths_count);
|
std::vector<std::unordered_set<size_t>> dependencies(paths_count);
|
||||||
for (size_t path_idx = 0; path_idx < paths_count; path_idx++) {
|
for (size_t curr_path_idx = 0; curr_path_idx < paths_count; ++curr_path_idx) {
|
||||||
for (size_t next_path_idx = path_idx + 1; next_path_idx < paths_count; next_path_idx++) {
|
for (size_t next_path_idx = curr_path_idx + 1; next_path_idx < paths_count; ++next_path_idx) {
|
||||||
if (paths_touch(distancers[path_idx], distancers[next_path_idx])) {
|
const BoundingBox &curr_path_bbox = bboxes[curr_path_idx];
|
||||||
dependencies[next_path_idx].insert(path_idx);
|
const BoundingBox &next_path_bbox = bboxes[next_path_idx];
|
||||||
|
|
||||||
|
if (bbox_bbox_distance(curr_path_bbox, next_path_bbox) >= touch_distance_threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (are_paths_touching(distancers[curr_path_idx], curr_path_bbox, distancers[next_path_idx], next_path_bbox, touch_distance_threshold)) {
|
||||||
|
dependencies[next_path_idx].insert(curr_path_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,6 +148,6 @@ void sort_paths(RandomAccessIterator begin, RandomAccessIterator end, Point star
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}} // namespace Slic3r::Algorithm
|
} // namespace Slic3r::Algorithm
|
||||||
|
|
||||||
#endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/
|
#endif /*SRC_LIBSLIC3R_PATH_SORTING_HPP_*/
|
||||||
|
@ -134,7 +134,7 @@ static std::vector<const PerimeterExtrusion *> ordered_perimeter_extrusions_to_m
|
|||||||
const Point &extrusion_start_position = extrusion_line.junctions.front().p;
|
const Point &extrusion_start_position = extrusion_line.junctions.front().p;
|
||||||
const double distance_sqr = (current_position - extrusion_start_position).cast<double>().squaredNorm();
|
const double distance_sqr = (current_position - extrusion_start_position).cast<double>().squaredNorm();
|
||||||
if (distance_sqr < nearest_distance_sqr) {
|
if (distance_sqr < nearest_distance_sqr) {
|
||||||
if (extrusion_line.is_closed || (!extrusion_line.is_closed && nearest_distance_sqr != std::numeric_limits<double>::max()) || (!extrusion_line.is_closed && !is_nearest_closed)) {
|
if (extrusion_line.is_closed || (!extrusion_line.is_closed && nearest_distance_sqr == std::numeric_limits<double>::max()) || (!extrusion_line.is_closed && !is_nearest_closed)) {
|
||||||
nearest_extrusion_idx = extrusion_idx;
|
nearest_extrusion_idx = extrusion_idx;
|
||||||
nearest_distance_sqr = distance_sqr;
|
nearest_distance_sqr = distance_sqr;
|
||||||
is_nearest_closed = extrusion_line.is_closed;
|
is_nearest_closed = extrusion_line.is_closed;
|
||||||
@ -190,7 +190,7 @@ static std::vector<size_t> order_of_grouped_perimeter_extrusions_to_minimize_dis
|
|||||||
const Point &extrusion_start_position = external_perimeter_extrusion_line.junctions.front().p;
|
const Point &extrusion_start_position = external_perimeter_extrusion_line.junctions.front().p;
|
||||||
const double distance_sqr = (current_position - extrusion_start_position).cast<double>().squaredNorm();
|
const double distance_sqr = (current_position - extrusion_start_position).cast<double>().squaredNorm();
|
||||||
if (distance_sqr < nearest_distance_sqr) {
|
if (distance_sqr < nearest_distance_sqr) {
|
||||||
if (external_perimeter_extrusion_line.is_closed || (!external_perimeter_extrusion_line.is_closed && nearest_distance_sqr != std::numeric_limits<double>::max()) || (!external_perimeter_extrusion_line.is_closed && !is_nearest_closed)) {
|
if (external_perimeter_extrusion_line.is_closed || (!external_perimeter_extrusion_line.is_closed && nearest_distance_sqr == std::numeric_limits<double>::max()) || (!external_perimeter_extrusion_line.is_closed && !is_nearest_closed)) {
|
||||||
nearest_grouped_extrusions_idx = grouped_extrusion_idx;
|
nearest_grouped_extrusions_idx = grouped_extrusion_idx;
|
||||||
nearest_distance_sqr = distance_sqr;
|
nearest_distance_sqr = distance_sqr;
|
||||||
is_nearest_closed = external_perimeter_extrusion_line.is_closed;
|
is_nearest_closed = external_perimeter_extrusion_line.is_closed;
|
||||||
@ -225,33 +225,32 @@ static PerimeterExtrusions extract_ordered_perimeter_extrusions(const PerimeterE
|
|||||||
while (!stack.empty()) {
|
while (!stack.empty()) {
|
||||||
const PerimeterExtrusion *current_extrusion = stack.top();
|
const PerimeterExtrusion *current_extrusion = stack.top();
|
||||||
const size_t current_extrusion_idx = current_extrusion - sorted_perimeter_extrusions.data();
|
const size_t current_extrusion_idx = current_extrusion - sorted_perimeter_extrusions.data();
|
||||||
|
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
visited[current_extrusion_idx] = true;
|
||||||
|
|
||||||
if (visited[current_extrusion_idx])
|
if (current_extrusion->nearest_external_perimeter == &perimeter_extrusion) {
|
||||||
continue;
|
|
||||||
|
|
||||||
if (current_extrusion->nearest_external_perimeter == &perimeter_extrusion)
|
|
||||||
grouped_extrusions.back().extrusions.emplace_back(current_extrusion);
|
grouped_extrusions.back().extrusions.emplace_back(current_extrusion);
|
||||||
|
}
|
||||||
|
|
||||||
if (current_extrusion->adjacent_perimeter_extrusions.size() == 1) {
|
|
||||||
const PerimeterExtrusion *adjacent_extrusion = current_extrusion->adjacent_perimeter_extrusions.front();
|
|
||||||
stack.push(adjacent_extrusion);
|
|
||||||
} else if (current_extrusion->adjacent_perimeter_extrusions.size() > 1) {
|
|
||||||
// When there is more than one available candidate, then order candidates to minimize distances between
|
|
||||||
// candidates and also to minimize the distance from the current_position.
|
|
||||||
std::vector<const PerimeterExtrusion *> available_candidates;
|
std::vector<const PerimeterExtrusion *> available_candidates;
|
||||||
for (const PerimeterExtrusion *adjacent_extrusion : current_extrusion->adjacent_perimeter_extrusions) {
|
for (const PerimeterExtrusion *adjacent_extrusion : current_extrusion->adjacent_perimeter_extrusions) {
|
||||||
if (const size_t adjacent_extrusion_idx = adjacent_extrusion - sorted_perimeter_extrusions.data(); !visited[adjacent_extrusion_idx])
|
const size_t adjacent_extrusion_idx = adjacent_extrusion - sorted_perimeter_extrusions.data();
|
||||||
|
if (!visited[adjacent_extrusion_idx] && !adjacent_extrusion->is_external_perimeter() && adjacent_extrusion->nearest_external_perimeter == &perimeter_extrusion) {
|
||||||
available_candidates.emplace_back(adjacent_extrusion);
|
available_candidates.emplace_back(adjacent_extrusion);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const PerimeterExtrusion *> adjacent_extrusions = ordered_perimeter_extrusions_to_minimize_distances(Point::Zero(), available_candidates);
|
|
||||||
std::reverse(adjacent_extrusions.begin(), adjacent_extrusions.end());
|
|
||||||
for (const PerimeterExtrusion *adjacent_extrusion : adjacent_extrusions)
|
|
||||||
stack.push(adjacent_extrusion);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
visited[current_extrusion_idx] = true;
|
if (available_candidates.size() == 1) {
|
||||||
|
stack.push(available_candidates.front());
|
||||||
|
} else if (available_candidates.size() > 1) {
|
||||||
|
// When there is more than one available candidate, then order candidates to minimize distances between
|
||||||
|
// candidates and also to minimize the distance from the current_position.
|
||||||
|
std::vector<const PerimeterExtrusion *> adjacent_extrusions = ordered_perimeter_extrusions_to_minimize_distances(Point::Zero(), available_candidates);
|
||||||
|
for (auto extrusion_it = adjacent_extrusions.rbegin(); extrusion_it != adjacent_extrusions.rend(); ++extrusion_it) {
|
||||||
|
stack.push(*extrusion_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!external_perimeters_first)
|
if (!external_perimeters_first)
|
||||||
|
@ -328,6 +328,23 @@ inline double bbox_point_distance_squared(const BoundingBox &bbox, const Point &
|
|||||||
coord_t(0));
|
coord_t(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Minimum distance between two Bounding boxes.
|
||||||
|
// Returns zero when Bounding boxes overlap.
|
||||||
|
inline double bbox_bbox_distance(const BoundingBox &first_bbox, const BoundingBox &second_bbox) {
|
||||||
|
if (first_bbox.overlap(second_bbox))
|
||||||
|
return 0.;
|
||||||
|
|
||||||
|
double bboxes_distance_squared = 0.;
|
||||||
|
|
||||||
|
for (size_t axis = 0; axis < 2; ++axis) {
|
||||||
|
const coord_t axis_distance = std::max(std::max(0, first_bbox.min[axis] - second_bbox.max[axis]),
|
||||||
|
second_bbox.min[axis] - first_bbox.max[axis]);
|
||||||
|
bboxes_distance_squared += Slic3r::sqr(static_cast<double>(axis_distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::sqrt(bboxes_distance_squared);
|
||||||
|
}
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
BoundingBoxBase<Vec<2, T>> to_2d(const BoundingBox3Base<Vec<3, T>> &bb)
|
BoundingBoxBase<Vec<2, T>> to_2d(const BoundingBox3Base<Vec<3, T>> &bb)
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,244 @@
|
|||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
const constexpr coord_t MAX_LINE_LENGTH_TO_FILTER = scaled<coord_t>(4.); // 4 mm.
|
||||||
|
const constexpr size_t MAX_SKIPS_ALLOWED = 2; // Skip means propagation through long line.
|
||||||
|
const constexpr size_t MIN_DEPTH_FOR_LINE_REMOVING = 5;
|
||||||
|
|
||||||
|
struct LineNode
|
||||||
|
{
|
||||||
|
struct State
|
||||||
|
{
|
||||||
|
// The total number of long lines visited before this node was reached.
|
||||||
|
// We just need the minimum number of all possible paths to decide whether we can remove the line or not.
|
||||||
|
int min_skips_taken = 0;
|
||||||
|
// The total number of short lines visited before this node was reached.
|
||||||
|
int total_short_lines = 0;
|
||||||
|
// Some initial line is touching some long line. This information is propagated to neighbors.
|
||||||
|
bool initial_touches_long_lines = false;
|
||||||
|
bool initialized = false;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
this->min_skips_taken = 0;
|
||||||
|
this->total_short_lines = 0;
|
||||||
|
this->initial_touches_long_lines = false;
|
||||||
|
this->initialized = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit LineNode(const Line &line) : line(line) {}
|
||||||
|
|
||||||
|
Line line;
|
||||||
|
// Pointers to line nodes in the previous and the next section that overlap with this line.
|
||||||
|
std::vector<LineNode*> next_section_overlapping_lines;
|
||||||
|
std::vector<LineNode*> prev_section_overlapping_lines;
|
||||||
|
|
||||||
|
bool is_removed = false;
|
||||||
|
|
||||||
|
State state;
|
||||||
|
|
||||||
|
// Return true if some initial line is touching some long line and this information was propagated into the current line.
|
||||||
|
bool is_initial_line_touching_long_lines() const {
|
||||||
|
if (prev_section_overlapping_lines.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (LineNode *line_node : prev_section_overlapping_lines) {
|
||||||
|
if (line_node->state.initial_touches_long_lines)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if the current line overlaps with some long line in the previous section.
|
||||||
|
bool is_touching_long_lines_in_previous_layer() const {
|
||||||
|
if (prev_section_overlapping_lines.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (LineNode *line_node : prev_section_overlapping_lines) {
|
||||||
|
if (!line_node->is_removed && line_node->line.length() >= MAX_LINE_LENGTH_TO_FILTER)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if the current line overlaps with some line in the next section.
|
||||||
|
bool has_next_layer_neighbours() const {
|
||||||
|
if (next_section_overlapping_lines.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (LineNode *line_node : next_section_overlapping_lines) {
|
||||||
|
if (!line_node->is_removed)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using LineNodes = std::vector<LineNode>;
|
||||||
|
|
||||||
|
inline bool are_lines_overlapping_in_y_axes(const Line &first_line, const Line &second_line) {
|
||||||
|
return (second_line.a.y() <= first_line.a.y() && first_line.a.y() <= second_line.b.y())
|
||||||
|
|| (second_line.a.y() <= first_line.b.y() && first_line.b.y() <= second_line.b.y())
|
||||||
|
|| (first_line.a.y() <= second_line.a.y() && second_line.a.y() <= first_line.b.y())
|
||||||
|
|| (first_line.a.y() <= second_line.b.y() && second_line.b.y() <= first_line.b.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_line_note_be_removed(const LineNode &line_node) {
|
||||||
|
return (line_node.line.length() < MAX_LINE_LENGTH_TO_FILTER)
|
||||||
|
&& (line_node.state.total_short_lines > int(MIN_DEPTH_FOR_LINE_REMOVING)
|
||||||
|
|| (!line_node.is_initial_line_touching_long_lines() && !line_node.has_next_layer_neighbours()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the node and propagate its removal to the previous sections.
|
||||||
|
void propagate_line_node_remove(const LineNode &line_node) {
|
||||||
|
std::queue<LineNode *> line_node_queue;
|
||||||
|
for (LineNode *prev_line : line_node.prev_section_overlapping_lines) {
|
||||||
|
if (prev_line->is_removed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
line_node_queue.emplace(prev_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; !line_node_queue.empty(); line_node_queue.pop()) {
|
||||||
|
LineNode &line_to_check = *line_node_queue.front();
|
||||||
|
|
||||||
|
if (can_line_note_be_removed(line_to_check)) {
|
||||||
|
line_to_check.is_removed = true;
|
||||||
|
|
||||||
|
for (LineNode *prev_line : line_to_check.prev_section_overlapping_lines) {
|
||||||
|
if (prev_line->is_removed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
line_node_queue.emplace(prev_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out short extrusions that could create vibrations.
|
||||||
|
static std::vector<Lines> filter_vibrating_extrusions(const std::vector<Lines> &lines_sections) {
|
||||||
|
// Initialize all line nodes.
|
||||||
|
std::vector<LineNodes> line_nodes_sections(lines_sections.size());
|
||||||
|
for (const Lines &lines_section : lines_sections) {
|
||||||
|
const size_t section_idx = &lines_section - lines_sections.data();
|
||||||
|
|
||||||
|
line_nodes_sections[section_idx].reserve(lines_section.size());
|
||||||
|
for (const Line &line : lines_section) {
|
||||||
|
line_nodes_sections[section_idx].emplace_back(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precalculate for each line node which line nodes in the previous and next section this line node overlaps.
|
||||||
|
for (auto curr_lines_section_it = line_nodes_sections.begin(); curr_lines_section_it != line_nodes_sections.end(); ++curr_lines_section_it) {
|
||||||
|
if (curr_lines_section_it != line_nodes_sections.begin()) {
|
||||||
|
const auto prev_lines_section_it = std::prev(curr_lines_section_it);
|
||||||
|
for (LineNode &curr_line : *curr_lines_section_it) {
|
||||||
|
for (LineNode &prev_line : *prev_lines_section_it) {
|
||||||
|
if (are_lines_overlapping_in_y_axes(curr_line.line, prev_line.line)) {
|
||||||
|
curr_line.prev_section_overlapping_lines.emplace_back(&prev_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::next(curr_lines_section_it) != line_nodes_sections.end()) {
|
||||||
|
const auto next_lines_section_it = std::next(curr_lines_section_it);
|
||||||
|
for (LineNode &curr_line : *curr_lines_section_it) {
|
||||||
|
for (LineNode &next_line : *next_lines_section_it) {
|
||||||
|
if (are_lines_overlapping_in_y_axes(curr_line.line, next_line.line)) {
|
||||||
|
curr_line.next_section_overlapping_lines.emplace_back(&next_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select each section as the initial lines section and propagate line node states from this initial lines section to the last lines section.
|
||||||
|
// During this propagation, we remove those lines that meet the conditions for its removal.
|
||||||
|
// When some line is removed, we propagate this removal to previous layers.
|
||||||
|
for (size_t initial_line_section_idx = 0; initial_line_section_idx < line_nodes_sections.size(); ++initial_line_section_idx) {
|
||||||
|
// Stars from non-removed short lines.
|
||||||
|
for (LineNode &initial_line : line_nodes_sections[initial_line_section_idx]) {
|
||||||
|
if (initial_line.is_removed || initial_line.line.length() >= MAX_LINE_LENGTH_TO_FILTER)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
initial_line.state.reset();
|
||||||
|
initial_line.state.total_short_lines = 1;
|
||||||
|
initial_line.state.initial_touches_long_lines = initial_line.is_touching_long_lines_in_previous_layer();
|
||||||
|
initial_line.state.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate from the initial lines section until the last lines section.
|
||||||
|
for (size_t propagation_line_section_idx = initial_line_section_idx; propagation_line_section_idx < line_nodes_sections.size(); ++propagation_line_section_idx) {
|
||||||
|
// Before we propagate node states into next lines sections, we reset the state of all line nodes in the next line section.
|
||||||
|
if (propagation_line_section_idx + 1 < line_nodes_sections.size()) {
|
||||||
|
for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx + 1]) {
|
||||||
|
propagation_line.state.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (LineNode &propagation_line : line_nodes_sections[propagation_line_section_idx]) {
|
||||||
|
if (propagation_line.is_removed || !propagation_line.state.initialized)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (LineNode *neighbour_line : propagation_line.next_section_overlapping_lines) {
|
||||||
|
if (neighbour_line->is_removed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const bool is_short_line = neighbour_line->line.length() < MAX_LINE_LENGTH_TO_FILTER;
|
||||||
|
const bool is_skip_allowed = propagation_line.state.min_skips_taken < int(MAX_SKIPS_ALLOWED);
|
||||||
|
|
||||||
|
if (!is_short_line && !is_skip_allowed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const int neighbour_total_short_lines = propagation_line.state.total_short_lines + int(is_short_line);
|
||||||
|
const int neighbour_min_skips_taken = propagation_line.state.min_skips_taken + int(!is_short_line);
|
||||||
|
|
||||||
|
if (neighbour_line->state.initialized) {
|
||||||
|
// When the state of the node was previously filled, then we need to update data in such a way
|
||||||
|
// that will maximize the possibility of removing this node.
|
||||||
|
neighbour_line->state.min_skips_taken = std::max(neighbour_line->state.min_skips_taken, neighbour_total_short_lines);
|
||||||
|
neighbour_line->state.min_skips_taken = std::min(neighbour_line->state.min_skips_taken, neighbour_min_skips_taken);
|
||||||
|
|
||||||
|
// We will keep updating neighbor initial_touches_long_lines until it is equal to false.
|
||||||
|
if (neighbour_line->state.initial_touches_long_lines) {
|
||||||
|
neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
neighbour_line->state.total_short_lines = neighbour_total_short_lines;
|
||||||
|
neighbour_line->state.min_skips_taken = neighbour_min_skips_taken;
|
||||||
|
neighbour_line->state.initial_touches_long_lines = propagation_line.state.initial_touches_long_lines;
|
||||||
|
neighbour_line->state.initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (can_line_note_be_removed(propagation_line)) {
|
||||||
|
// Remove the current node and propagate its removal to the previous sections.
|
||||||
|
propagation_line.is_removed = true;
|
||||||
|
propagate_line_node_remove(propagation_line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create lines sections without filtered-out lines.
|
||||||
|
std::vector<Lines> lines_sections_out(line_nodes_sections.size());
|
||||||
|
for (const std::vector<LineNode> &line_nodes_section : line_nodes_sections) {
|
||||||
|
const size_t section_idx = &line_nodes_section - line_nodes_sections.data();
|
||||||
|
|
||||||
|
for (const LineNode &line_node : line_nodes_section) {
|
||||||
|
if (!line_node.is_removed) {
|
||||||
|
lines_sections_out[section_idx].emplace_back(line_node.line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines_sections_out;
|
||||||
|
}
|
||||||
|
|
||||||
ThickPolylines make_fill_polylines(
|
ThickPolylines make_fill_polylines(
|
||||||
const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions)
|
const Fill *fill, const Surface *surface, const FillParams ¶ms, bool stop_vibrations, bool fill_gaps, bool connect_extrusions)
|
||||||
{
|
{
|
||||||
@ -49,11 +287,6 @@ ThickPolylines make_fill_polylines(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto segments_overlap = [](coord_t alow, coord_t ahigh, coord_t blow, coord_t bhigh) {
|
|
||||||
return (alow >= blow && alow <= bhigh) || (ahigh >= blow && ahigh <= bhigh) || (blow >= alow && blow <= ahigh) ||
|
|
||||||
(bhigh >= alow && bhigh <= ahigh);
|
|
||||||
};
|
|
||||||
|
|
||||||
const coord_t scaled_spacing = scaled<coord_t>(fill->spacing);
|
const coord_t scaled_spacing = scaled<coord_t>(fill->spacing);
|
||||||
double distance_limit_reconnection = 2.0 * double(scaled_spacing);
|
double distance_limit_reconnection = 2.0 * double(scaled_spacing);
|
||||||
double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection;
|
double squared_distance_limit_reconnection = distance_limit_reconnection * distance_limit_reconnection;
|
||||||
@ -71,22 +304,23 @@ ThickPolylines make_fill_polylines(
|
|||||||
AABBTreeLines::LinesDistancer<Line> area_walls{to_lines(inner_area)};
|
AABBTreeLines::LinesDistancer<Line> area_walls{to_lines(inner_area)};
|
||||||
|
|
||||||
const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing;
|
const size_t n_vlines = (bb.max.x() - bb.min.x() + scaled_spacing - 1) / scaled_spacing;
|
||||||
std::vector<Line> vertical_lines(n_vlines);
|
const coord_t y_min = bb.min.y();
|
||||||
coord_t y_min = bb.min.y();
|
const coord_t y_max = bb.max.y();
|
||||||
coord_t y_max = bb.max.y();
|
Lines vertical_lines(n_vlines);
|
||||||
|
|
||||||
for (size_t i = 0; i < n_vlines; i++) {
|
for (size_t i = 0; i < n_vlines; i++) {
|
||||||
coord_t x = bb.min.x() + i * double(scaled_spacing);
|
coord_t x = bb.min.x() + i * double(scaled_spacing);
|
||||||
vertical_lines[i].a = Point{x, y_min};
|
vertical_lines[i].a = Point{x, y_min};
|
||||||
vertical_lines[i].b = Point{x, y_max};
|
vertical_lines[i].b = Point{x, y_max};
|
||||||
}
|
}
|
||||||
if (vertical_lines.size() > 0) {
|
|
||||||
|
if (!vertical_lines.empty()) {
|
||||||
vertical_lines.push_back(vertical_lines.back());
|
vertical_lines.push_back(vertical_lines.back());
|
||||||
vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min};
|
vertical_lines.back().a = Point{coord_t(bb.min.x() + n_vlines * double(scaled_spacing) + scaled_spacing * 0.5), y_min};
|
||||||
vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max};
|
vertical_lines.back().b = Point{vertical_lines.back().a.x(), y_max};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::vector<Line>> polygon_sections(n_vlines);
|
std::vector<Lines> polygon_sections(n_vlines);
|
||||||
|
|
||||||
for (size_t i = 0; i < n_vlines; i++) {
|
for (size_t i = 0; i < n_vlines; i++) {
|
||||||
const auto intersections = area_walls.intersections_with_line<true>(vertical_lines[i]);
|
const auto intersections = area_walls.intersections_with_line<true>(vertical_lines[i]);
|
||||||
|
|
||||||
@ -102,87 +336,7 @@ ThickPolylines make_fill_polylines(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stop_vibrations) {
|
if (stop_vibrations) {
|
||||||
struct Node
|
polygon_sections = filter_vibrating_extrusions(polygon_sections);
|
||||||
{
|
|
||||||
int section_idx;
|
|
||||||
int line_idx;
|
|
||||||
int skips_taken = 0;
|
|
||||||
bool neighbours_explored = false;
|
|
||||||
std::vector<std::pair<int, int>> neighbours{};
|
|
||||||
};
|
|
||||||
|
|
||||||
coord_t length_filter = scale_(4);
|
|
||||||
size_t skips_allowed = 2;
|
|
||||||
size_t min_removal_conut = 5;
|
|
||||||
for (int section_idx = 0; section_idx < int(polygon_sections.size()); ++ section_idx) {
|
|
||||||
for (int line_idx = 0; line_idx < int(polygon_sections[section_idx].size()); ++ line_idx) {
|
|
||||||
if (const Line &line = polygon_sections[section_idx][line_idx]; line.a != line.b && line.length() < length_filter) {
|
|
||||||
std::set<std::pair<int, int>> to_remove{{section_idx, line_idx}};
|
|
||||||
std::vector<Node> to_visit{{section_idx, line_idx}};
|
|
||||||
|
|
||||||
bool initial_touches_long_lines = false;
|
|
||||||
if (section_idx > 0) {
|
|
||||||
for (int prev_line_idx = 0; prev_line_idx < int(polygon_sections[section_idx - 1].size()); ++ prev_line_idx) {
|
|
||||||
if (const Line &nl = polygon_sections[section_idx - 1][prev_line_idx];
|
|
||||||
nl.a != nl.b && segments_overlap(line.a.y(), line.b.y(), nl.a.y(), nl.b.y())) {
|
|
||||||
initial_touches_long_lines = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!to_visit.empty()) {
|
|
||||||
Node curr = to_visit.back();
|
|
||||||
const Line &curr_l = polygon_sections[curr.section_idx][curr.line_idx];
|
|
||||||
if (curr.neighbours_explored) {
|
|
||||||
bool is_valid_for_removal = (curr_l.length() < length_filter) &&
|
|
||||||
((int(to_remove.size()) - curr.skips_taken > int(min_removal_conut)) ||
|
|
||||||
(curr.neighbours.empty() && !initial_touches_long_lines));
|
|
||||||
if (!is_valid_for_removal) {
|
|
||||||
for (const auto &n : curr.neighbours) {
|
|
||||||
if (to_remove.find(n) != to_remove.end()) {
|
|
||||||
is_valid_for_removal = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!is_valid_for_removal) {
|
|
||||||
to_remove.erase({curr.section_idx, curr.line_idx});
|
|
||||||
}
|
|
||||||
to_visit.pop_back();
|
|
||||||
} else {
|
|
||||||
to_visit.back().neighbours_explored = true;
|
|
||||||
int curr_index = to_visit.size() - 1;
|
|
||||||
bool can_use_skip = curr_l.length() <= length_filter && curr.skips_taken < int(skips_allowed);
|
|
||||||
if (curr.section_idx + 1 < int(polygon_sections.size())) {
|
|
||||||
for (int lidx = 0; lidx < int(polygon_sections[curr.section_idx + 1].size()); ++ lidx) {
|
|
||||||
if (const Line &nl = polygon_sections[curr.section_idx + 1][lidx];
|
|
||||||
nl.a != nl.b && segments_overlap(curr_l.a.y(), curr_l.b.y(), nl.a.y(), nl.b.y()) &&
|
|
||||||
(nl.length() < length_filter || can_use_skip)) {
|
|
||||||
to_visit[curr_index].neighbours.push_back({curr.section_idx + 1, lidx});
|
|
||||||
to_remove.insert({curr.section_idx + 1, lidx});
|
|
||||||
Node next_node{curr.section_idx + 1, lidx, curr.skips_taken + (nl.length() >= length_filter)};
|
|
||||||
to_visit.push_back(next_node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &pair : to_remove) {
|
|
||||||
Line &l = polygon_sections[pair.first][pair.second];
|
|
||||||
l.a = l.b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t section_idx = 0; section_idx < polygon_sections.size(); section_idx++) {
|
|
||||||
polygon_sections[section_idx].erase(std::remove_if(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
|
|
||||||
[](const Line &s) { return s.a == s.b; }),
|
|
||||||
polygon_sections[section_idx].end());
|
|
||||||
std::sort(polygon_sections[section_idx].begin(), polygon_sections[section_idx].end(),
|
|
||||||
[](const Line &a, const Line &b) { return a.a.y() < b.b.y(); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThickPolylines thick_polylines;
|
ThickPolylines thick_polylines;
|
||||||
|
@ -194,7 +194,7 @@ template<class L> bool intersection(const L &l1, const L &l2, Vec<Dim<L>, Scalar
|
|||||||
class Line
|
class Line
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Line() {}
|
Line() = default;
|
||||||
Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
|
Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
|
||||||
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
|
explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
|
||||||
void scale(double factor) { this->a *= factor; this->b *= factor; }
|
void scale(double factor) { this->a *= factor; this->b *= factor; }
|
||||||
|
@ -201,6 +201,10 @@ bool Polyline::is_straight() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundingBox ThickPolyline::bounding_box() const {
|
||||||
|
return BoundingBox(this->points);
|
||||||
|
}
|
||||||
|
|
||||||
BoundingBox get_extents(const Polyline &polyline)
|
BoundingBox get_extents(const Polyline &polyline)
|
||||||
{
|
{
|
||||||
return polyline.bounding_box();
|
return polyline.bounding_box();
|
||||||
@ -297,6 +301,60 @@ std::pair<int, Point> foot_pt(const Points &polyline, const Point &pt)
|
|||||||
return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min);
|
return std::make_pair(int(it_proj - polyline.begin()) - 1, foot_pt_min);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t total_lines_count(const ThickPolylines &thick_polylines) {
|
||||||
|
size_t lines_cnt = 0;
|
||||||
|
for (const ThickPolyline &thick_polyline : thick_polylines) {
|
||||||
|
if (thick_polyline.points.size() > 1) {
|
||||||
|
lines_cnt += thick_polyline.points.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lines to_lines(const ThickPolyline &thick_polyline) {
|
||||||
|
Lines lines;
|
||||||
|
if (thick_polyline.points.size() >= 2) {
|
||||||
|
lines.reserve(thick_polyline.points.size() - 1);
|
||||||
|
|
||||||
|
for (Points::const_iterator it = thick_polyline.points.begin(); it != thick_polyline.points.end() - 1; ++it) {
|
||||||
|
lines.emplace_back(*it, *(it + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lines to_lines(const ThickPolylines &thick_polylines) {
|
||||||
|
const size_t lines_cnt = total_lines_count(thick_polylines);
|
||||||
|
|
||||||
|
Lines lines;
|
||||||
|
lines.reserve(lines_cnt);
|
||||||
|
for (const ThickPolyline &thick_polyline : thick_polylines) {
|
||||||
|
for (Points::const_iterator it = thick_polyline.points.begin(); it != thick_polyline.points.end() - 1; ++it) {
|
||||||
|
lines.emplace_back(*it, *(it + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundingBox get_extents(const ThickPolyline &thick_polyline) {
|
||||||
|
return thick_polyline.bounding_box();
|
||||||
|
}
|
||||||
|
|
||||||
|
BoundingBox get_extents(const ThickPolylines &thick_polylines) {
|
||||||
|
BoundingBox bbox;
|
||||||
|
if (!thick_polylines.empty()) {
|
||||||
|
bbox = thick_polylines.front().bounding_box();
|
||||||
|
for (size_t i = 1; i < thick_polylines.size(); ++i) {
|
||||||
|
bbox.merge(thick_polylines[i].points);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
|
||||||
ThickLines ThickPolyline::thicklines() const
|
ThickLines ThickPolyline::thicklines() const
|
||||||
{
|
{
|
||||||
ThickLines lines;
|
ThickLines lines;
|
||||||
|
@ -119,30 +119,40 @@ inline double total_length(const Polylines &polylines) {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Lines to_lines(const Polyline &poly)
|
inline size_t total_lines_count(const Polylines &polylines) {
|
||||||
{
|
size_t lines_cnt = 0;
|
||||||
|
for (const Polyline &polyline : polylines) {
|
||||||
|
if (polyline.points.size() > 1) {
|
||||||
|
lines_cnt += polyline.points.size() - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Lines to_lines(const Polyline &poly) {
|
||||||
Lines lines;
|
Lines lines;
|
||||||
if (poly.points.size() >= 2) {
|
if (poly.points.size() >= 2) {
|
||||||
lines.reserve(poly.points.size() - 1);
|
lines.reserve(poly.points.size() - 1);
|
||||||
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end() - 1; ++it) {
|
||||||
lines.push_back(Line(*it, *(it + 1)));
|
lines.emplace_back(*it, *(it + 1));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Lines to_lines(const Polylines &polys)
|
inline Lines to_lines(const Polylines &polylines) {
|
||||||
{
|
const size_t lines_cnt = total_lines_count(polylines);
|
||||||
size_t n_lines = 0;
|
|
||||||
for (size_t i = 0; i < polys.size(); ++ i)
|
|
||||||
if (polys[i].points.size() > 1)
|
|
||||||
n_lines += polys[i].points.size() - 1;
|
|
||||||
Lines lines;
|
Lines lines;
|
||||||
lines.reserve(n_lines);
|
lines.reserve(lines_cnt);
|
||||||
for (size_t i = 0; i < polys.size(); ++ i) {
|
for (const Polyline &polyline : polylines) {
|
||||||
const Polyline &poly = polys[i];
|
for (Points::const_iterator it = polyline.points.begin(); it != polyline.points.end() - 1; ++it) {
|
||||||
for (Points::const_iterator it = poly.points.begin(); it != poly.points.end()-1; ++it)
|
lines.emplace_back(*it, *(it + 1));
|
||||||
lines.push_back(Line(*it, *(it + 1)));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,6 +241,8 @@ struct ThickPolyline {
|
|||||||
// On open ThickPolyline make no effect.
|
// On open ThickPolyline make no effect.
|
||||||
void start_at_index(int index);
|
void start_at_index(int index);
|
||||||
|
|
||||||
|
BoundingBox bounding_box() const;
|
||||||
|
|
||||||
Points points;
|
Points points;
|
||||||
// vector of startpoint width and endpoint width of each line segment. The size should be always (points.size()-1) * 2
|
// vector of startpoint width and endpoint width of each line segment. The size should be always (points.size()-1) * 2
|
||||||
// e.g. let four be points a,b,c,d. that are three lines ab, bc, cd. for each line, there should be start width, so the width vector is:
|
// e.g. let four be points a,b,c,d. that are three lines ab, bc, cd. for each line, there should be start width, so the width vector is:
|
||||||
@ -251,6 +263,14 @@ inline ThickPolylines to_thick_polylines(Polylines &&polylines, const coordf_t w
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t total_lines_count(const ThickPolylines &thick_polylines);
|
||||||
|
|
||||||
|
Lines to_lines(const ThickPolyline &thick_polyline);
|
||||||
|
Lines to_lines(const ThickPolylines &thick_polylines);
|
||||||
|
|
||||||
|
BoundingBox get_extents(const ThickPolyline &thick_polyline);
|
||||||
|
BoundingBox get_extents(const ThickPolylines &thick_polylines);
|
||||||
|
|
||||||
class Polyline3 : public MultiPoint3
|
class Polyline3 : public MultiPoint3
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user