diff --git a/src/libslic3r/GCode/ExtrusionProcessor.hpp b/src/libslic3r/GCode/ExtrusionProcessor.hpp index f95947baa..6dac88675 100644 --- a/src/libslic3r/GCode/ExtrusionProcessor.hpp +++ b/src/libslic3r/GCode/ExtrusionProcessor.hpp @@ -88,6 +88,126 @@ public: } }; +struct ExtendedPoint +{ + ExtendedPoint(Vec2d position, float distance = 0.0, size_t nearest_prev_layer_line = size_t(-1), float curvature = 0.0, float quality = 1.0) + : position(position), distance(distance), nearest_prev_layer_line(nearest_prev_layer_line), curvature(curvature), quality(quality) + {} + + Vec2d position; + float distance; + size_t nearest_prev_layer_line; + float curvature; + float quality; +}; + +template +std::vector estimate_points_properties(const std::vector

&extrusion_points, + const AABBTreeLines::LinesDistancer &unscaled_prev_layer, + float flow_width) +{ + if (extrusion_points.empty()) return {}; + float boundary_offset = PREV_LAYER_BOUNDARY_ONLY ? 0.5 * flow_width : 0.0f; + CurvatureEstimator cestim; + float min_malformation_dist = 0.2 * flow_width; + float peak_malformation_dist = 0.6 * flow_width; + + std::vector points; + points.reserve(extrusion_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); + auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; + + { + ExtendedPoint start_point{maybe_unscale(extrusion_points.begin())}; + auto [distance, nearest_line, x] = unscaled_prev_layer.signed_distance_from_lines_extra(start_point.position) + boundary_offset; + start_point.distance = distance; + start_point.nearest_prev_layer_line = nearest_line; + points.push_back(start_point); + } + for (size_t i = 1; i < extrusion_points.size(); i++) { + ExtendedPoint next_point{maybe_unscale(extrusion_points[i])}; + auto [distance, nearest_line, x] = unscaled_prev_layer.signed_distance_from_lines_extra(next_point.position) + boundary_offset; + next_point.distance = distance; + next_point.nearest_prev_layer_line = nearest_line; + + if (ADD_INTERSECTIONS) { + const ExtendedPoint &prev_point = points.back(); + if ((prev_point.distance < min_malformation_dist) != (next_point.distance < min_malformation_dist)) { // one in air, one not + auto intersections = unscaled_prev_layer.intersections_with_line(L{prev_point.position, next_point.position}); + for (const auto &intersection : intersections) { points.push_back({intersection, boundary_offset, 1.0}); } + } + + if (PREV_LAYER_BOUNDARY_ONLY && prev_point.distance > min_malformation_dist && + next_point.distance > min_malformation_dist) { // both in air + double line_len = (prev_point.position - next_point.position).norm(); + if (line_len > 3.0) { + double a0 = std::clamp((boundary_offset + prev_point.distance) / line_len, 0.0, 1.0); + double a1 = std::clamp((boundary_offset + next_point.distance) / line_len, 0.0, 1.0); + double t0 = std::min(a0, a1); + double t1 = std::max(a0, a1); + + auto p0 = prev_point.position + t0 * (next_point.position - prev_point.position); + auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.signed_distance_from_lines(p0) + boundary_offset; + points.push_back(ExtendedPoint{p0, p0_dist, p0_near_l}); + + auto p1 = prev_point.position + t1 * (next_point.position - prev_point.position); + auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.signed_distance_from_lines(p1) + boundary_offset; + points.push_back(ExtendedPoint{p1, p1_dist, p1_near_l}); + } + } + } + points.push_back(next_point); + } + + for (int point_idx = 0; point_idx < points.size(); ++point_idx) { + ExtendedPoint &a = points[point_idx]; + ExtendedPoint &prev = points[point_idx > 0 ? point_idx - 1 : point_idx]; + + int prev_point_idx = point_idx; + while (prev_point_idx > 0) { + prev_point_idx--; + if ((a.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; } + } + + int next_point_index = point_idx; + while (next_point_index < int(points.size()) - 1) { + next_point_index++; + if ((a.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; } + } + + if (prev_point_idx != point_idx && next_point_index != point_idx) { + float distance = (prev.position - a.position).norm(); + float alfa = angle(a.position - points[prev_point_idx].position, points[next_point_index].position - a.position); + if (alfa > 0.95 * 0.5 * PI) { + alfa = 0; // Ignore very sharp corners.. The curling problem happens mostly on rounded surfaces, not sudden sharp turns + } + cestim.add_point(distance, alfa); + if (CONCAVITY_RESETS_CURVATURE && alfa < 0.0) { cestim.reset(); } + } + + if (a.distance < min_malformation_dist) { + a.quality = 1.0; + cestim.reset(); + } else { + float distance_quality = std::min(1.0f, std::abs(a.distance - peak_malformation_dist) / + (peak_malformation_dist - min_malformation_dist)); + distance_quality = distance_quality * distance_quality; + + float curvature_penalty = 0.0f; + a.curvature = cestim.get_curvature(); + float curvature = std::abs(a.curvature); + if (curvature > 1.0f) { + curvature_penalty = 1.0f; + } else if (curvature > 0.1f) { + curvature_penalty = sqrt(1.0 - distance_quality) * curvature; + } + + a.quality = std::clamp(distance_quality - curvature_penalty, 0.0f, 1.0f); + } + } + + return points; +} + struct ProcessedPoint { Point p; @@ -98,129 +218,32 @@ class ExtrusionQualityEstimator { std::unordered_map> prev_layer_boundaries; std::unordered_map> next_layer_boundaries; - CurvatureEstimator cestim; const PrintObject *current_object; public: - void reset_for_next_extrusion() { cestim.reset(); } - void set_current_object(const PrintObject *object) { current_object = object; } void prepare_for_new_layer(const Layer *layer) { if (layer == nullptr) return; - const PrintObject *object = layer->object(); + const PrintObject *object = layer->object(); prev_layer_boundaries[object] = next_layer_boundaries[object]; next_layer_boundaries[object] = AABBTreeLines::LinesDistancer{to_unscaled_linesf(layer->lslices)}; } std::vector estimate_extrusion_quality(const ExtrusionPath &path) { - struct ExtendedPoint - { - ExtendedPoint(const Vec2d &pos, float dist, float quality) : position(pos), distance(dist), quality(quality) {} - - Vec2d position; - float distance; - float quality; - }; - - float flow_width = path.width; - float min_malformation_dist = 0.2 * flow_width; - float peak_malformation_dist = 0.6 * flow_width; - - const Points &original_points = path.polyline.points; - std::vector points; - - const auto& prev_layer_boundary = prev_layer_boundaries[current_object]; - - float distance = prev_layer_boundary.signed_distance_from_lines(unscaled(original_points[0])) + 0.5 * flow_width; - points.push_back({unscaled(original_points[0]), distance, 1.0f}); - for (size_t i = 1; i < original_points.size(); i++) { - Vec2d next_point_pos = unscaled(original_points[i]); - float distance_of_next = prev_layer_boundary.signed_distance_from_lines(next_point_pos) + 0.5 * flow_width; - if ((points.back().distance < min_malformation_dist) != (distance_of_next < min_malformation_dist)) { // one in air, one not - auto intersections = prev_layer_boundary.intersections_with_line({points.back().position, next_point_pos}); - for (const auto &intersection : intersections) { points.push_back({intersection, 0.5f * flow_width, 1.0}); } - points.push_back({next_point_pos, distance_of_next, 1.0}); - } - - if (points.back().distance > min_malformation_dist && distance_of_next > min_malformation_dist) { // both in air - double line_len = (points.back().position - next_point_pos).norm(); - if (line_len > 3.0) { - double a0 = std::clamp((0.5 * flow_width + points.back().distance) / line_len, 0.0, 1.0); - double a1 = std::clamp((0.5 * flow_width + distance_of_next) / line_len, 0.0, 1.0); - double t0 = std::min(a0, a1); - double t1 = std::max(a0, a1); - - auto p0 = points.back().position + t0 * (next_point_pos - points.back().position); - auto p0_dist = prev_layer_boundary.signed_distance_from_lines(p0) + 0.5 * flow_width; - points.push_back({p0, float(p0_dist), 1.0}); - auto p1 = points.back().position + t1 * (next_point_pos - points.back().position); - auto p1_dist = prev_layer_boundary.signed_distance_from_lines(p1) + 0.5 * flow_width; - points.push_back({p1, float(p1_dist), 1.0}); - } - } - - points.push_back({next_point_pos, distance_of_next, 1.0}); + std::vector extended_points = + estimate_points_properties(path.polyline.points, prev_layer_boundaries[current_object], path.width); + + std::vector processed_points; + processed_points.reserve(extended_points.size()); + for (size_t i = 0; i < extended_points.size(); i++) { + Point position = scaled(extended_points[i].position); + float speed_factor = std::min(extended_points[i].quality, extended_points[i+1].quality); + processed_points.push_back({position, speed_factor}); } - - for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) { - ExtendedPoint &a = points[point_idx]; - ExtendedPoint &b = points[point_idx + 1]; - - float distance = std::min(a.distance, b.distance); - - int prev_point_idx = point_idx; - while (prev_point_idx > 0) { - prev_point_idx--; - if ((b.position - points[prev_point_idx].position).squaredNorm() > EPSILON) { break; } - } - - int next_point_index = point_idx; - while (next_point_index < int(points.size()) - 1) { - next_point_index++; - if ((b.position - points[next_point_index].position).squaredNorm() > EPSILON) { break; } - } - - if (prev_point_idx != point_idx && next_point_index != point_idx) { - float distance = (b.position - a.position).norm(); - float alfa = angle(b.position - points[prev_point_idx].position, points[next_point_index].position - b.position); - if (alfa > 0.9 * PI / 2.0) { - alfa = 0; // Ignore very sharp corners.. The curling problem happens mostly on rounded surfaces, not sudden sharp turns - } - cestim.add_point(distance, alfa); - } - - if (distance < min_malformation_dist) { - a.quality = 1.0; - cestim.reset(); - } else { - float distance_quality = std::min(1.0f, std::abs(distance - peak_malformation_dist) / - (peak_malformation_dist - min_malformation_dist)); - distance_quality = distance_quality * distance_quality; - - float curvature_penalty = 0.0f; - float curvature = std::abs(cestim.get_curvature()); - if (curvature > 1.0f) { - curvature_penalty = 1.0f; - } else if (curvature > 0.1f) { - curvature_penalty = sqrt(1.0 - distance_quality) * curvature; - } - - a.quality = std::clamp(distance_quality - curvature_penalty, 0.0f, 1.0f); - } - } - - if (points.size() > 3) { - points[points.size() - 2].quality = points[points.size()-3].quality; - } - - std::vector result; - result.reserve(points.size()); - for (const ExtendedPoint &p : points) { result.push_back({Point::new_scale(p.position), std::clamp(p.quality, 0.0f, 1.0f)}); } - - return result; + return processed_points; } };