mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-12 21:18:58 +08:00
Compuation of overhang distance of the perimeters is a new step in the slicing pipeline, the result is stored as and ExtrusionPath attribute.
Perimeters are split as necessary based on the differing distances.
This commit is contained in:
parent
28cd3ac212
commit
6ec918c2a2
@ -345,7 +345,7 @@ public:
|
||||
return dist;
|
||||
}
|
||||
|
||||
std::vector<size_t> all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius)
|
||||
std::vector<size_t> all_lines_in_radius(const Vec<2, Scalar> &point, Floating radius) const
|
||||
{
|
||||
return AABBTreeLines::all_lines_in_radius(this->lines, this->tree, point.template cast<Floating>(), radius * radius);
|
||||
}
|
||||
|
@ -142,6 +142,8 @@ set(SLIC3R_SOURCES
|
||||
GCode/ConflictChecker.hpp
|
||||
GCode/CoolingBuffer.cpp
|
||||
GCode/CoolingBuffer.hpp
|
||||
GCode/ExtrusionProcessor.cpp
|
||||
GCode/ExtrusionProcessor.hpp
|
||||
GCode/FindReplace.cpp
|
||||
GCode/FindReplace.hpp
|
||||
GCode/GCodeWriter.cpp
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "Polyline.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <numeric>
|
||||
|
||||
@ -101,6 +102,11 @@ inline bool operator==(const ExtrusionFlow &lhs, const ExtrusionFlow &rhs)
|
||||
return lhs.mm3_per_mm == rhs.mm3_per_mm && lhs.width == rhs.width && lhs.height == rhs.height;
|
||||
}
|
||||
|
||||
struct OverhangAttributes {
|
||||
float max_distance_from_prev_layer;
|
||||
float proximity_to_curled_lines; //value between 0 and 1
|
||||
};
|
||||
|
||||
struct ExtrusionAttributes : ExtrusionFlow
|
||||
{
|
||||
ExtrusionAttributes() = default;
|
||||
@ -110,6 +116,9 @@ struct ExtrusionAttributes : ExtrusionFlow
|
||||
|
||||
// What is the role / purpose of this extrusion?
|
||||
ExtrusionRole role{ ExtrusionRole::None };
|
||||
// OVerhangAttributes are currently computed for perimeters if dynamic overhangs are enabled.
|
||||
// They are used to control fan and print speed in export.
|
||||
std::optional<OverhangAttributes> overhang_attributes;
|
||||
};
|
||||
|
||||
inline bool operator==(const ExtrusionAttributes &lhs, const ExtrusionAttributes &rhs)
|
||||
|
130
src/libslic3r/GCode/ExtrusionProcessor.cpp
Normal file
130
src/libslic3r/GCode/ExtrusionProcessor.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
#include "ExtrusionProcessor.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path,
|
||||
const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer,
|
||||
const AABBTreeLines::LinesDistancer<CurledLine> &prev_layer_curled_lines)
|
||||
{
|
||||
std::vector<ExtendedPoint> extended_points = estimate_points_properties<true, true, true, true>(path.polyline.points,
|
||||
unscaled_prev_layer, path.width());
|
||||
std::vector<std::pair<float, float>> calculated_distances(extended_points.size());
|
||||
|
||||
for (size_t i = 0; i < extended_points.size(); i++) {
|
||||
const ExtendedPoint &curr = extended_points[i];
|
||||
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
|
||||
|
||||
// The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines
|
||||
float proximity_to_curled_lines = 0.0;
|
||||
const double dist_limit = 10.0 * path.width();
|
||||
{
|
||||
Vec2d middle = 0.5 * (curr.position + next.position);
|
||||
auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit));
|
||||
if (!line_indices.empty()) {
|
||||
double len = (next.position - curr.position).norm();
|
||||
// For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle
|
||||
// of this long line
|
||||
// The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down.
|
||||
// NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point
|
||||
// TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge
|
||||
if (len > 8) {
|
||||
Vec2d dir = Vec2d(next.position - curr.position) / len;
|
||||
Vec2d right = Vec2d(-dir.y(), dir.x());
|
||||
|
||||
Polygon box_of_influence = {
|
||||
scaled(Vec2d(curr.position + right * dist_limit)),
|
||||
scaled(Vec2d(next.position + right * dist_limit)),
|
||||
scaled(Vec2d(next.position - right * dist_limit)),
|
||||
scaled(Vec2d(curr.position - right * dist_limit)),
|
||||
};
|
||||
|
||||
double projected_lengths_sum = 0;
|
||||
for (size_t idx : line_indices) {
|
||||
const CurledLine &line = prev_layer_curled_lines.get_line(idx);
|
||||
Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence});
|
||||
if (inside.empty())
|
||||
continue;
|
||||
double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast<double>()))));
|
||||
projected_lengths_sum += projected_length;
|
||||
}
|
||||
if (projected_lengths_sum < 0.4 * len) {
|
||||
line_indices.clear();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t idx : line_indices) {
|
||||
const CurledLine &line = prev_layer_curled_lines.get_line(idx);
|
||||
float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle)));
|
||||
float proximity = (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) *
|
||||
(line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator
|
||||
proximity_to_curled_lines = std::max(proximity_to_curled_lines, proximity);
|
||||
}
|
||||
}
|
||||
}
|
||||
calculated_distances[i].first = std::max(curr.distance, next.distance);
|
||||
calculated_distances[i].second = proximity_to_curled_lines;
|
||||
}
|
||||
|
||||
ExtrusionPaths result;
|
||||
ExtrusionAttributes new_attrs = path.attributes();
|
||||
new_attrs.overhang_attributes = std::optional<OverhangAttributes>({calculated_distances[0].first, calculated_distances[0].second});
|
||||
result.emplace_back(new_attrs);
|
||||
result.back().polyline.append(Point::new_scale(extended_points[0].position));
|
||||
for (size_t i = 1; i < extended_points.size(); i++) {
|
||||
if (std::abs(calculated_distances[i].first - calculated_distances[i - 1].first) < path.width() * 0.1 &&
|
||||
std::abs(calculated_distances[i].second - calculated_distances[i - 1].second) < 0.1) {
|
||||
// do not start new path, the attributes are similar enough
|
||||
} else {
|
||||
result.back().polyline.append(Point::new_scale(extended_points[i].position));
|
||||
new_attrs.overhang_attributes->max_distance_from_prev_layer = calculated_distances[i].first;
|
||||
new_attrs.overhang_attributes->proximity_to_curled_lines = calculated_distances[i].second;
|
||||
result.emplace_back(new_attrs);
|
||||
}
|
||||
result.back().polyline.append(Point::new_scale(extended_points[i].position));
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc,
|
||||
const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer,
|
||||
const AABBTreeLines::LinesDistancer<CurledLine> &prev_layer_curled_lines)
|
||||
{
|
||||
ExtrusionEntityCollection result;
|
||||
result.no_sort = ecc.no_sort;
|
||||
for (const auto *e : ecc.entities) {
|
||||
if (auto *col = static_cast<const ExtrusionEntityCollection *>(e)) {
|
||||
auto new_col = calculate_and_split_overhanging_extrusions(*col, unscaled_prev_layer, prev_layer_curled_lines);
|
||||
result.append(new_col);
|
||||
} else if (auto *loop = static_cast<const ExtrusionLoop *>(e)) {
|
||||
ExtrusionLoop new_loop = *loop;
|
||||
new_loop.paths.clear();
|
||||
for (const ExtrusionPath &p : loop->paths) {
|
||||
auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines);
|
||||
new_loop.paths.insert(new_loop.paths.end(), paths.begin(), paths.end());
|
||||
}
|
||||
result.append(new_loop);
|
||||
} else if (auto *mp = static_cast<const ExtrusionMultiPath *>(e)) {
|
||||
ExtrusionMultiPath new_mp = *mp;
|
||||
new_mp.paths.clear();
|
||||
for (const ExtrusionPath &p : mp->paths) {
|
||||
auto paths = calculate_and_split_overhanging_extrusions(p, unscaled_prev_layer, prev_layer_curled_lines);
|
||||
new_mp.paths.insert(new_mp.paths.end(), paths.begin(), paths.end());
|
||||
}
|
||||
result.append(new_mp);
|
||||
} else if (auto *op = static_cast<const ExtrusionPathOriented *>(e)) {
|
||||
auto paths = calculate_and_split_overhanging_extrusions(*op, unscaled_prev_layer, prev_layer_curled_lines);
|
||||
for (const ExtrusionPath &p : paths) {
|
||||
result.append(ExtrusionPathOriented(p.polyline, p.attributes()));
|
||||
}
|
||||
} else if (auto *p = static_cast<const ExtrusionPath *>(e)) {
|
||||
auto paths = calculate_and_split_overhanging_extrusions(*p, unscaled_prev_layer, prev_layer_curled_lines);
|
||||
result.append(paths);
|
||||
} else {
|
||||
throw Slic3r::InvalidArgument("Unknown extrusion entity type");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
@ -14,6 +14,7 @@
|
||||
#include "../Flow.hpp"
|
||||
#include "../Config.hpp"
|
||||
#include "../Line.hpp"
|
||||
#include "../Exception.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@ -21,6 +22,7 @@
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@ -211,106 +213,29 @@ std::vector<ExtendedPoint> estimate_points_properties(const POINTS
|
||||
return points;
|
||||
}
|
||||
|
||||
void calculate_and_split_overhanging_extrusions(ExtrusionPath &path,
|
||||
AABBTreeLines::LinesDistancer<Line> prev_layer_polygon,
|
||||
AABBTreeLines::LinesDistancer<CurledLine> prev_layer_curled_lines)
|
||||
{
|
||||
std::vector<ExtendedPoint> extended_points = estimate_points_properties<true, true, true, true>(path.polyline.points,
|
||||
prev_layer_polygon, path.width());
|
||||
|
||||
ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path,
|
||||
const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer,
|
||||
const AABBTreeLines::LinesDistancer<CurledLine> &prev_layer_curled_lines);
|
||||
|
||||
for (size_t i = 0; i < extended_points.size(); i++) {
|
||||
const ExtendedPoint &curr = extended_points[i];
|
||||
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
|
||||
|
||||
// The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines
|
||||
float artificial_distance_to_curled_lines = 0.0;
|
||||
const double dist_limit = 10.0 * path.width();
|
||||
{
|
||||
Vec2d middle = 0.5 * (curr.position + next.position);
|
||||
auto line_indices = prev_layer_curled_lines.all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit));
|
||||
if (!line_indices.empty()) {
|
||||
double len = (next.position - curr.position).norm();
|
||||
// For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle
|
||||
// of this long line
|
||||
// The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down.
|
||||
// NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point
|
||||
// TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge
|
||||
if (len > 8) {
|
||||
Vec2d dir = Vec2d(next.position - curr.position) / len;
|
||||
Vec2d right = Vec2d(-dir.y(), dir.x());
|
||||
|
||||
Polygon box_of_influence = {
|
||||
scaled(Vec2d(curr.position + right * dist_limit)),
|
||||
scaled(Vec2d(next.position + right * dist_limit)),
|
||||
scaled(Vec2d(next.position - right * dist_limit)),
|
||||
scaled(Vec2d(curr.position - right * dist_limit)),
|
||||
};
|
||||
|
||||
double projected_lengths_sum = 0;
|
||||
for (size_t idx : line_indices) {
|
||||
const CurledLine &line = prev_layer_curled_lines.get_line(idx);
|
||||
Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence});
|
||||
if (inside.empty())
|
||||
continue;
|
||||
double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast<double>()))));
|
||||
projected_lengths_sum += projected_length;
|
||||
}
|
||||
if (projected_lengths_sum < 0.4 * len) {
|
||||
line_indices.clear();
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t idx : line_indices) {
|
||||
const CurledLine &line = prev_layer_curled_lines.get_line(idx);
|
||||
float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle)));
|
||||
float dist = path.width() * (1.0 - (distance_from_curled / dist_limit)) * (1.0 - (distance_from_curled / dist_limit)) *
|
||||
(line.curled_height / (path.height() * 10.0f)); // max_curled_height_factor from SupportSpotGenerator
|
||||
artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto interpolate_speed = [](const std::map<float, float> &values, float distance) {
|
||||
auto upper_dist = values.lower_bound(distance);
|
||||
if (upper_dist == values.end()) {
|
||||
return values.rbegin()->second;
|
||||
}
|
||||
if (upper_dist == values.begin()) {
|
||||
return upper_dist->second;
|
||||
}
|
||||
|
||||
auto lower_dist = std::prev(upper_dist);
|
||||
float t = (distance - lower_dist->first) / (upper_dist->first - lower_dist->first);
|
||||
return (1.0f - t) * lower_dist->second + t * upper_dist->second;
|
||||
};
|
||||
|
||||
float extrusion_speed = std::min(interpolate_speed(speed_sections, curr.distance), interpolate_speed(speed_sections, next.distance));
|
||||
float curled_base_speed = interpolate_speed(speed_sections, artificial_distance_to_curled_lines);
|
||||
float final_speed = std::min(curled_base_speed, extrusion_speed);
|
||||
float fan_speed = std::min(interpolate_speed(fan_speed_sections, curr.distance),
|
||||
interpolate_speed(fan_speed_sections, next.distance));
|
||||
|
||||
processed_points.push_back({scaled(curr.position), final_speed, int(fan_speed)});
|
||||
}
|
||||
return processed_points;
|
||||
}
|
||||
|
||||
ExtrusionEntityCollection calculate_and_split_overhanging_extrusions(const ExtrusionEntityCollection &ecc,
|
||||
const AABBTreeLines::LinesDistancer<Linef> &unscaled_prev_layer,
|
||||
const AABBTreeLines::LinesDistancer<CurledLine> &prev_layer_curled_lines);
|
||||
|
||||
struct ProcessedPoint
|
||||
{
|
||||
Point p;
|
||||
float speed = 1.0f;
|
||||
int fan_speed = 0;
|
||||
float speed = 1.0f;
|
||||
int fan_speed = 0;
|
||||
};
|
||||
|
||||
class ExtrusionQualityEstimator
|
||||
{
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> prev_layer_boundaries;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<Linef>> next_layer_boundaries;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> prev_curled_extrusions;
|
||||
std::unordered_map<const PrintObject *, AABBTreeLines::LinesDistancer<CurledLine>> next_curled_extrusions;
|
||||
const PrintObject *current_object;
|
||||
const PrintObject *current_object;
|
||||
|
||||
public:
|
||||
void set_current_object(const PrintObject *object) { current_object = object; }
|
||||
@ -338,17 +263,18 @@ public:
|
||||
float speed_base = ext_perimeter_speed > 0 ? ext_perimeter_speed : original_speed;
|
||||
std::map<float, float> speed_sections;
|
||||
for (size_t i = 0; i < overhangs_w_speeds.size(); i++) {
|
||||
float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0));
|
||||
float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) :
|
||||
overhangs_w_speeds[i].second.value;
|
||||
if (speed < EPSILON) speed = speed_base;
|
||||
float distance = flow.width * (1.0 - (overhangs_w_speeds[i].first / 100.0));
|
||||
float speed = overhangs_w_speeds[i].second.percent ? (speed_base * overhangs_w_speeds[i].second.value / 100.0) :
|
||||
overhangs_w_speeds[i].second.value;
|
||||
if (speed < EPSILON)
|
||||
speed = speed_base;
|
||||
speed_sections[distance] = speed;
|
||||
}
|
||||
|
||||
std::map<float, float> fan_speed_sections;
|
||||
for (size_t i = 0; i < overhangs_w_fan_speeds.size(); i++) {
|
||||
float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0));
|
||||
float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id);
|
||||
float distance = flow.width * (1.0 - (overhangs_w_fan_speeds[i].first / 100.0));
|
||||
float fan_speed = overhangs_w_fan_speeds[i].second.get_at(extruder_id);
|
||||
fan_speed_sections[distance] = fan_speed;
|
||||
}
|
||||
|
||||
@ -362,15 +288,17 @@ public:
|
||||
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
|
||||
|
||||
// The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines
|
||||
float artificial_distance_to_curled_lines = 0.0;
|
||||
const double dist_limit = 10.0 * flow.width;
|
||||
float artificial_distance_to_curled_lines = 0.0;
|
||||
const double dist_limit = 10.0 * flow.width;
|
||||
{
|
||||
Vec2d middle = 0.5 * (curr.position + next.position);
|
||||
auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit));
|
||||
if (!line_indices.empty()) {
|
||||
double len = (next.position - curr.position).norm();
|
||||
// For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line
|
||||
// The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down.
|
||||
double len = (next.position - curr.position).norm();
|
||||
// For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near
|
||||
// the middle of this long line
|
||||
// The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing
|
||||
// down.
|
||||
// NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point
|
||||
// TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge
|
||||
if (len > 8) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "ExPolygon.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "GCode/ExtrusionProcessor.hpp"
|
||||
#include "KDTreeIndirect.hpp"
|
||||
#include "Line.hpp"
|
||||
#include "Point.hpp"
|
||||
@ -548,16 +549,21 @@ void PrintObject::calculate_overhanging_perimeters()
|
||||
m_print->set_status(89, _u8L("Calculating overhanging perimeters"));
|
||||
|
||||
std::unordered_map<size_t, AABBTreeLines::LinesDistancer<CurledLine>> curled_lines;
|
||||
std::unordered_map<size_t, AABBTreeLines::LinesDistancer<Linef>> unscaled_polygons_lines;
|
||||
for (const Layer *l : this->layers()) {
|
||||
curled_lines[l->id()] = AABBTreeLines::LinesDistancer<CurledLine>{l->curled_lines};
|
||||
curled_lines[l->id()] = AABBTreeLines::LinesDistancer<CurledLine>{l->curled_lines};
|
||||
unscaled_polygons_lines[l->id()] = AABBTreeLines::LinesDistancer<Linef>{to_unscaled_linesf(l->lslices)};
|
||||
}
|
||||
|
||||
for (Layer *l : this->layers()) {
|
||||
for (const LayerRegion *layer_region : l->regions()) {
|
||||
for (LayerRegion *layer_region : l->regions()) {
|
||||
if (regions_with_dynamic_overhangs.find(layer_region->m_region) == regions_with_dynamic_overhangs.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ExPolygons prev_layer_polygon = l->lower_layer == nullptr ? ExPolygons() : l->lower_layer->lslices;
|
||||
layer_region->m_perimeters = calculate_and_split_overhanging_extrusions(layer_region->m_perimeters,
|
||||
unscaled_polygons_lines[l->id()],
|
||||
curled_lines[l->id()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user