///|/ Copyright (c) Prusa Research 2022 - 2023 Pavel Mikuš @Godrak, Vojtěch Bubník @bubnikv ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #ifndef slic3r_ExtrusionProcessor_hpp_ #define slic3r_ExtrusionProcessor_hpp_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libslic3r/AABBTreeLines.hpp" #include "libslic3r/SupportSpotsGenerator.hpp" #include "libslic3r/libslic3r.h" #include "libslic3r/ExtrusionEntity.hpp" #include "libslic3r/Layer.hpp" #include "libslic3r/Point.hpp" #include "libslic3r/SVG.hpp" #include "libslic3r/BoundingBox.hpp" #include "libslic3r/Polygon.hpp" #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Flow.hpp" #include "libslic3r/Config.hpp" #include "libslic3r/Line.hpp" #include "libslic3r/Exception.hpp" #include "libslic3r/PrintConfig.hpp" #include "libslic3r/ExtrusionEntityCollection.hpp" namespace Slic3r { class CurledLine; class Linef; } // namespace Slic3r namespace Slic3r { namespace ExtrusionProcessor { struct ExtendedPoint { Vec2d position; float distance; float curvature; }; template std::vector estimate_points_properties(const POINTS &input_points, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, float flow_width, float max_line_length = -1.0f) { bool looped = input_points.front() == input_points.back(); std::function get_prev_index = [](size_t idx, size_t count) { if (idx > 0) { return idx - 1; } else return idx; }; if (looped) { get_prev_index = [](size_t idx, size_t count) { if (idx == 0) idx = count; return --idx; }; }; std::function get_next_index = [](size_t idx, size_t size) { if (idx + 1 < size) { return idx + 1; } else return idx; }; if (looped) { get_next_index = [](size_t idx, size_t count) { if (++idx == count) idx = 0; return idx; }; }; using P = typename POINTS::value_type; using AABBScalar = typename AABBTreeLines::LinesDistancer::Scalar; if (input_points.empty()) return {}; float boundary_offset = PREV_LAYER_BOUNDARY_OFFSET ? 0.5 * flow_width : 0.0f; auto maybe_unscale = [](const P &p) { return SCALED_INPUT ? unscaled(p) : p.template cast(); }; std::vector points; points.reserve(input_points.size() * (ADD_INTERSECTIONS ? 1.5 : 1)); { ExtendedPoint start_point{maybe_unscale(input_points.front())}; auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(start_point.position.cast()); start_point.distance = distance + boundary_offset; points.push_back(start_point); } for (size_t i = 1; i < input_points.size(); i++) { ExtendedPoint next_point{maybe_unscale(input_points[i])}; auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra(next_point.position.cast()); next_point.distance = distance + boundary_offset; if (ADD_INTERSECTIONS && ((points.back().distance > boundary_offset + EPSILON) != (next_point.distance > boundary_offset + EPSILON))) { const ExtendedPoint &prev_point = points.back(); auto intersections = unscaled_prev_layer.template intersections_with_line( L{prev_point.position.cast(), next_point.position.cast()}); for (const auto &intersection : intersections) { ExtendedPoint p{}; p.position = intersection.first.template cast(); p.distance = boundary_offset; points.push_back(p); } } points.push_back(next_point); } if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) { std::vector new_points; new_points.reserve(points.size() * 2); new_points.push_back(points.front()); for (int point_idx = 0; point_idx < int(points.size()) - 1; ++point_idx) { const ExtendedPoint &curr = points[point_idx]; const ExtendedPoint &next = points[point_idx + 1]; if ((curr.distance > -boundary_offset && curr.distance < boundary_offset + 2.0f) || (next.distance > -boundary_offset && next.distance < boundary_offset + 2.0f)) { double line_len = (next.position - curr.position).norm(); if (line_len > 4.0f) { double a0 = std::clamp((curr.distance + 3 * boundary_offset) / line_len, 0.0, 1.0); double a1 = std::clamp(1.0f - (next.distance + 3 * boundary_offset) / line_len, 0.0, 1.0); double t0 = std::min(a0, a1); double t1 = std::max(a0, a1); if (t0 < 1.0) { auto p0 = curr.position + t0 * (next.position - curr.position); auto [p0_dist, p0_near_l, p0_x] = unscaled_prev_layer.template distance_from_lines_extra(p0.cast()); ExtendedPoint new_p{}; new_p.position = p0; new_p.distance = float(p0_dist + boundary_offset); new_points.push_back(new_p); } if (t1 > 0.0) { auto p1 = curr.position + t1 * (next.position - curr.position); auto [p1_dist, p1_near_l, p1_x] = unscaled_prev_layer.template distance_from_lines_extra(p1.cast()); ExtendedPoint new_p{}; new_p.position = p1; new_p.distance = float(p1_dist + boundary_offset); new_points.push_back(new_p); } } } new_points.push_back(next); } points = std::move(new_points); } if (max_line_length > 0) { std::vector new_points; new_points.reserve(points.size() * 2); { for (size_t i = 0; i + 1 < points.size(); i++) { const ExtendedPoint &curr = points[i]; const ExtendedPoint &next = points[i + 1]; new_points.push_back(curr); double len = (next.position - curr.position).squaredNorm(); double t = sqrt((max_line_length * max_line_length) / len); size_t new_point_count = 1.0 / t; for (size_t j = 1; j < new_point_count + 1; j++) { Vec2d pos = curr.position * (1.0 - j * t) + next.position * (j * t); auto [p_dist, p_near_l, p_x] = unscaled_prev_layer.template distance_from_lines_extra(pos.cast()); ExtendedPoint new_p{}; new_p.position = pos; new_p.distance = float(p_dist + boundary_offset); new_points.push_back(new_p); } } new_points.push_back(points.back()); } points = std::move(new_points); } float accumulated_distance = 0; std::vector distances_for_curvature(points.size()); for (size_t point_idx = 0; point_idx < points.size(); ++point_idx) { const ExtendedPoint &a = points[point_idx]; const ExtendedPoint &b = points[get_prev_index(point_idx, points.size())]; distances_for_curvature[point_idx] = (b.position - a.position).norm(); accumulated_distance += distances_for_curvature[point_idx]; } if (accumulated_distance > EPSILON) for (float window_size : {3.0f, 9.0f, 16.0f}) { for (int point_idx = 0; point_idx < int(points.size()); ++point_idx) { ExtendedPoint ¤t = points[point_idx]; Vec2d back_position = current.position; { size_t back_point_index = point_idx; float dist_backwards = 0; while (dist_backwards < window_size * 0.5 && back_point_index != get_prev_index(back_point_index, points.size())) { float line_dist = distances_for_curvature[get_prev_index(back_point_index, points.size())]; if (dist_backwards + line_dist > window_size * 0.5) { back_position = points[back_point_index].position + (window_size * 0.5 - dist_backwards) * (points[get_prev_index(back_point_index, points.size())].position - points[back_point_index].position) .normalized(); dist_backwards += window_size * 0.5 - dist_backwards + EPSILON; } else { dist_backwards += line_dist; back_point_index = get_prev_index(back_point_index, points.size()); } } } Vec2d front_position = current.position; { size_t front_point_index = point_idx; float dist_forwards = 0; while (dist_forwards < window_size * 0.5 && front_point_index != get_next_index(front_point_index, points.size())) { float line_dist = distances_for_curvature[front_point_index]; if (dist_forwards + line_dist > window_size * 0.5) { front_position = points[front_point_index].position + (window_size * 0.5 - dist_forwards) * (points[get_next_index(front_point_index, points.size())].position - points[front_point_index].position) .normalized(); dist_forwards += window_size * 0.5 - dist_forwards + EPSILON; } else { dist_forwards += line_dist; front_point_index = get_next_index(front_point_index, points.size()); } } } float new_curvature = angle(current.position - back_position, front_position - current.position) / window_size; if (abs(current.curvature) < abs(new_curvature)) { current.curvature = new_curvature; } } } return points; } ExtrusionPaths calculate_and_split_overhanging_extrusions(const ExtrusionPath &path, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); ExtrusionEntityCollection calculate_and_split_overhanging_extrusions( const ExtrusionEntityCollection *ecc, const AABBTreeLines::LinesDistancer &unscaled_prev_layer, const AABBTreeLines::LinesDistancer &prev_layer_curled_lines); std::pair calculate_overhang_speed(const ExtrusionAttributes &attributes, const FullPrintConfig &config, size_t extruder_id, float external_perim_reference_speed, float default_speed); }} // namespace Slic3r::ExtrusionProcessor #endif // slic3r_ExtrusionProcessor_hpp_