#ifndef slic3r_BridgeDetector_hpp_ #define slic3r_BridgeDetector_hpp_ #include "ClipperUtils.hpp" #include "Line.hpp" #include "Point.hpp" #include "Polygon.hpp" #include "Polyline.hpp" #include "PrincipalComponents2D.hpp" #include "libslic3r.h" #include "ExPolygon.hpp" #include namespace Slic3r { // The bridge detector optimizes a direction of bridges over a region or a set of regions. // A bridge direction is considered optimal, if the length of the lines strang over the region is maximal. // This is optimal if the bridge is supported in a single direction only, but // it may not likely be optimal, if the bridge region is supported from all sides. Then an optimal // solution would find a direction with shortest bridges. // The bridge orientation is measured CCW from the X axis. class BridgeDetector { public: // The non-grown holes. const ExPolygons &expolygons; // In case the caller gaves us the input polygons by a value, make a copy. ExPolygons expolygons_owned; // Lower slices, all regions. const ExPolygons &lower_slices; // Scaled extrusion width of the infill. coord_t spacing; // Angle resolution for the brute force search of the best bridging angle. double resolution; // The final optimal angle. double angle; BridgeDetector(ExPolygon _expolygon, const ExPolygons &_lower_slices, coord_t _extrusion_width); BridgeDetector(const ExPolygons &_expolygons, const ExPolygons &_lower_slices, coord_t _extrusion_width); // If bridge_direction_override != 0, then the angle is used instead of auto-detect. bool detect_angle(double bridge_direction_override = 0.); // Coverage is currently only used by the unit tests. It is extremely slow and unreliable! Polygons coverage(double angle = -1) const; void unsupported_edges(double angle, Polylines* unsupported) const; Polylines unsupported_edges(double angle = -1) const; private: // Suppress warning "assignment operator could not be generated" BridgeDetector& operator=(const BridgeDetector &); void initialize(); struct BridgeDirection { BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.), archored_percent(0.){} // the best direction is the one causing most lines to be bridged (thus most coverage) bool operator<(const BridgeDirection &other) const { // Initial sort by coverage only - comparator must obey strict weak ordering return this->coverage > other.coverage;//this->archored_percent > other.archored_percent; }; double angle; double coverage; double max_length; double archored_percent; }; // Get possible briging direction candidates. std::vector bridge_direction_candidates() const; // Open lines representing the supporting edges. Polylines _edges; // Closed polygons representing the supporting areas. ExPolygons _anchor_regions; }; //return ideal bridge direction and unsupported bridge endpoints distance. inline std::tuple detect_bridging_direction(const Lines &floating_edges, const Polygons &overhang_area) { if (floating_edges.empty()) { // consider this area anchored from all sides, pick bridging direction that will likely yield shortest bridges auto [pc1, pc2] = compute_principal_components(overhang_area); if (pc2 == Vec2f::Zero()) { // overhang may be smaller than resolution. In this case, any direction is ok return {Vec2d{1.0,0.0}, 0.0}; } else { return {pc2.normalized().cast(), 0.0}; } } // Overhang is not fully surrounded by anchors, in that case, find such direction that will minimize the number of bridge ends/180turns in the air std::unordered_map directions{}; for (const Line &l : floating_edges) { Vec2d normal = l.normal().cast().normalized(); double quantized_angle = std::ceil(std::atan2(normal.y(),normal.x()) * 1000.0); directions.emplace(quantized_angle, normal); } std::vector> direction_costs{}; // it is acutally cost of a perpendicular bridge direction - we find the minimal cost and then return the perpendicular dir for (const auto& d : directions) { direction_costs.emplace_back(d.second, 0.0); } for (const Line &l : floating_edges) { Vec2d line = (l.b - l.a).cast(); for (auto &dir_cost : direction_costs) { // the dot product already contains the length of the line. dir_cost.first is normalized. dir_cost.second += std::abs(line.dot(dir_cost.first)); } } Vec2d result_dir = Vec2d::Ones(); double min_cost = std::numeric_limits::max(); for (const auto &cost : direction_costs) { if (cost.second < min_cost) { // now flip the orientation back and return the direction of the bridge extrusions result_dir = Vec2d{cost.first.y(), -cost.first.x()}; min_cost = cost.second; } } return {result_dir, min_cost}; }; //return ideal bridge direction and unsupported bridge endpoints distance. inline std::tuple detect_bridging_direction(const Polygons &to_cover, const Polygons &anchors_area) { Polygons overhang_area = diff(to_cover, anchors_area); Polylines floating_polylines = diff_pl(to_polylines(overhang_area), expand(anchors_area, float(SCALED_EPSILON))); if (floating_polylines.empty()) { // consider this area anchored from all sides, pick bridging direction that will likely yield shortest bridges auto [pc1, pc2] = compute_principal_components(overhang_area); if (pc2 == Vec2f::Zero()) { // overhang may be smaller than resolution. In this case, any direction is ok return {Vec2d{1.0,0.0}, 0.0}; } else { return {pc2.normalized().cast(), 0.0}; } } // Overhang is not fully surrounded by anchors, in that case, find such direction that will minimize the number of bridge ends/180turns in the air Lines floating_edges = to_lines(floating_polylines); std::unordered_map directions{}; for (const Line &l : floating_edges) { Vec2d normal = l.normal().cast().normalized(); double quantized_angle = std::ceil(std::atan2(normal.y(),normal.x()) * 1000.0); directions.emplace(quantized_angle, normal); } std::vector> direction_costs{}; // it is acutally cost of a perpendicular bridge direction - we find the minimal cost and then return the perpendicular dir for (const auto& d : directions) { direction_costs.emplace_back(d.second, 0.0); } for (const Line &l : floating_edges) { Vec2d line = (l.b - l.a).cast(); for (auto &dir_cost : direction_costs) { // the dot product already contains the length of the line. dir_cost.first is normalized. dir_cost.second += std::abs(line.dot(dir_cost.first)); } } Vec2d result_dir = Vec2d::Ones(); double min_cost = std::numeric_limits::max(); for (const auto &cost : direction_costs) { if (cost.second < min_cost) { // now flip the orientation back and return the direction of the bridge extrusions result_dir = Vec2d{cost.first.y(), -cost.first.x()}; min_cost = cost.second; } } return {result_dir, min_cost}; }; } #endif