OrcaSlicer/src/libslic3r/BridgeDetector.hpp
Ocraftyone 3b7b10f72f
Port "No Unsupported Perimeters" feature from SS (#3189)
* first impl

* Properly handle extra bridges in `detect_surfaces_type()`

* Pass `perimeter_spacing` and `ext_perimeter_width` as parameters instead of instance property

* Make `process_no_bridge()` private

* Attempt to run `process_no_bridge()` in arachne

* Update `BridgeDetector::coverage` to give us more precise bridge coverage

Co-authored-by: supermerill <merill@free.fr>

* Fix bridge infill margin scaling

* Rename the option name as well as add tooltip

---------

Co-authored-by: Noisyfox <timemanager.rick@gmail.com>
Co-authored-by: supermerill <merill@free.fr>
Co-authored-by: SoftFever <softfeverever@gmail.com>
2024-01-28 10:12:55 +00:00

136 lines
5.8 KiB
C++

///|/ Copyright (c) Prusa Research 2016 - 2023 Pavel Mikuš @Godrak, Vojtěch Bubník @bubnikv
///|/ Copyright (c) Slic3r 2014 - 2015 Alessandro Ranellucci @alranel
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#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 <string>
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.);
Polygons coverage(double angle = -1, bool precise = true) 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<double> 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<Vec2d, double> 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<double>(), 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<double, Vec2d> directions{};
for (const Line &l : floating_edges) {
Vec2d normal = l.normal().cast<double>().normalized();
double quantized_angle = std::ceil(std::atan2(normal.y(),normal.x()) * 1000.0);
directions.emplace(quantized_angle, normal);
}
std::vector<std::pair<Vec2d, double>> 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<double>();
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<double>::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<Vec2d, double> detect_bridging_direction(const Polygons &to_cover, const Polygons &anchors_area)
{
Polygons overhang_area = diff(to_cover, anchors_area);
Lines floating_edges = to_lines(diff_pl(to_polylines(overhang_area), expand(anchors_area, float(SCALED_EPSILON))));
return detect_bridging_direction(floating_edges, overhang_area);
}
}
#endif