OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::BridgeDetector Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/BridgeDetector.hpp>

Collaboration diagram for Slic3r::BridgeDetector:

Classes

struct  BridgeDirection

Public Member Functions

 BridgeDetector (ExPolygon _expolygon, const ExPolygons &_lower_slices, coord_t _extrusion_width)
 BridgeDetector (const ExPolygons &_expolygons, const ExPolygons &_lower_slices, coord_t _extrusion_width)
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

Public Attributes

const ExPolygons & expolygons
ExPolygons expolygons_owned
const ExPolygons & lower_slices
coord_t spacing
double resolution
double angle

Private Member Functions

BridgeDetector & operator= (const BridgeDetector &)
void initialize ()
std::vector< double > bridge_direction_candidates () const

Private Attributes

Polylines _edges
ExPolygons _anchor_regions

Detailed Description

Constructor & Destructor Documentation

◆ BridgeDetector() [1/2]

Slic3r::BridgeDetector::BridgeDetector ( ExPolygon _expolygon,
const ExPolygons & _lower_slices,
coord_t _extrusion_width )
22 :
23 // The original infill polygon, not inflated.
25 // All surfaces of the object supporting this region.
26 lower_slices(_lower_slices),
27 spacing(_spacing)
28{
29 this->expolygons_owned.push_back(std::move(_expolygon));
30 initialize();
31}
const ExPolygons & lower_slices
Definition BridgeDetector.hpp:36
ExPolygons expolygons_owned
Definition BridgeDetector.hpp:34
coord_t spacing
Definition BridgeDetector.hpp:38
const ExPolygons & expolygons
Definition BridgeDetector.hpp:32
void initialize()
Definition BridgeDetector.cpp:46

References expolygons, expolygons_owned, initialize(), lower_slices, and spacing.

Referenced by operator=().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ BridgeDetector() [2/2]

Slic3r::BridgeDetector::BridgeDetector ( const ExPolygons & _expolygons,
const ExPolygons & _lower_slices,
coord_t _extrusion_width )
36 :
37 // The original infill polygon, not inflated.
38 expolygons(_expolygons),
39 // All surfaces of the object supporting this region.
40 lower_slices(_lower_slices),
41 spacing(_spacing)
42{
43 initialize();
44}

References expolygons, initialize(), lower_slices, and spacing.

Here is the call graph for this function:

Member Function Documentation

◆ bridge_direction_candidates()

std::vector< double > Slic3r::BridgeDetector::bridge_direction_candidates ( ) const
private
191{
192 // we test angles according to configured resolution
193 std::vector<double> angles;
194 for (int i = 0; i <= PI/this->resolution; ++i)
195 angles.push_back(i * this->resolution);
196
197 // we also test angles of each bridge contour
198 {
199 Lines lines = to_lines(this->expolygons);
200 for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
201 angles.push_back(line->direction());
202 }
203
204 /* we also test angles of each open supporting edge
205 (this finds the optimal angle for C-shaped supports) */
206 for (const Polyline &edge : this->_edges)
207 if (edge.first_point() != edge.last_point())
208 angles.push_back(Line(edge.first_point(), edge.last_point()).direction());
209
210 // remove duplicates
211 double min_resolution = PI/180.0; // 1 degree
212 std::sort(angles.begin(), angles.end());
213 for (size_t i = 1; i < angles.size(); ++i) {
214 if (Slic3r::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) {
215 angles.erase(angles.begin() + i);
216 --i;
217 }
218 }
219 /* compare first value with last one and remove the greatest one (PI)
220 in case they are parallel (PI, 0) */
221 if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
222 angles.pop_back();
223
224 return angles;
225}
Polylines _edges
Definition BridgeDetector.hpp:75
double resolution
Definition BridgeDetector.hpp:40
static constexpr double PI
Definition libslic3r.h:81
bool directions_parallel(double angle1, double angle2, double max_diff)
Definition Geometry.cpp:40
std::vector< Line > Lines
Definition Line.hpp:20
Lines to_lines(const ExPolygon &src)
Definition ExPolygon.hpp:124

References _edges, Slic3r::Line::direction(), Slic3r::Geometry::directions_parallel(), expolygons, PI, resolution, and Slic3r::to_lines().

Referenced by detect_angle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ coverage()

Polygons Slic3r::BridgeDetector::coverage ( double angle = -1,
bool precise = true ) const
336{
337 if (angle == -1)
338 angle = this->angle;
339
340 Polygons covered;
341
342 if (angle != -1) {
343 // Get anchors, convert them to Polygons and rotate them.
344 Polygons anchors = to_polygons(this->_anchor_regions);
345 polygons_rotate(anchors, PI / 2.0 - angle);
346 //same for region which do not need bridging
347 //Polygons supported_area = diff(this->lower_slices.expolygons, this->_anchor_regions, true);
348 //polygons_rotate(anchors, PI / 2.0 - angle);
349
350 for (ExPolygon expolygon : this->expolygons) {
351 // Clone our expolygon and rotate it so that we work with vertical lines.
352 expolygon.rotate(PI / 2.0 - angle);
353 // Outset the bridge expolygon by half the amount we used for detecting anchors;
354 // we'll use this one to generate our trapezoids and be sure that their vertices
355 // are inside the anchors and not on their contours leading to false negatives.
356 for (ExPolygon &expoly : offset_ex(expolygon, 0.5f * float(this->spacing))) {
357 // Compute trapezoids according to a vertical orientation
358 Polygons trapezoids;
359 if (!precise) get_trapezoids2(expoly, &trapezoids, PI / 2);
360 else get_trapezoids3_half(expoly, &trapezoids, float(this->spacing));
361 for (Polygon &trapezoid : trapezoids) {
362 size_t n_supported = 0;
363 if (!precise) {
364 // not nice, we need a more robust non-numeric check
365 // imporvment 1: take into account when we go in the supported area.
366 for (const Line &supported_line : intersection_ln(trapezoid.lines(), anchors))
367 if (supported_line.length() >= this->spacing)
368 ++n_supported;
369 } else {
370 Polygons intersects = intersection(Polygons{trapezoid}, anchors);
371 n_supported = intersects.size();
372
373 if (n_supported >= 2) {
374 // trim it to not allow to go outside of the intersections
375 BoundingBox center_bound = intersects[0].bounding_box();
376 coord_t min_y = center_bound.center()(1), max_y = center_bound.center()(1);
377 for (Polygon &poly_bound : intersects) {
378 center_bound = poly_bound.bounding_box();
379 if (min_y > center_bound.center()(1)) min_y = center_bound.center()(1);
380 if (max_y < center_bound.center()(1)) max_y = center_bound.center()(1);
381 }
382 coord_t min_x = trapezoid[0](0), max_x = trapezoid[0](0);
383 for (Point &p : trapezoid.points) {
384 if (min_x > p(0)) min_x = p(0);
385 if (max_x < p(0)) max_x = p(0);
386 }
387 //add what get_trapezoids3 has removed (+EPSILON)
388 min_x -= (this->spacing / 4 + 1);
389 max_x += (this->spacing / 4 + 1);
390 coord_t mid_x = (min_x + max_x) / 2;
391 for (Point &p : trapezoid.points) {
392 if (p(1) < min_y) p(1) = min_y;
393 if (p(1) > max_y) p(1) = max_y;
394 if (p(0) > min_x && p(0) < mid_x) p(0) = min_x;
395 if (p(0) < max_x && p(0) > mid_x) p(0) = max_x;
396 }
397 }
398 }
399
400 if (n_supported >= 2) {
401 //add it
402 covered.push_back(std::move(trapezoid));
403 }
404 }
405 }
406 }
407
408 // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
409 // instead of exact overlaps.
410 covered = union_(covered);
411 // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
412 polygons_rotate(covered, -(PI/2.0 - angle));
413 //covered = intersection(this->expolygons, covered);
414#if 0
415 {
416 my @lines = map @{$_->lines}, @$trapezoids;
417 $_->rotate(-(PI/2 - $angle), [0,0]) for @lines;
418
419 require "Slic3r/SVG.pm";
420 Slic3r::SVG::output(
421 "coverage_" . rad2deg($angle) . ".svg",
422 expolygons => [$self->expolygon],
423 green_expolygons => $self->_anchor_regions,
424 red_expolygons => $coverage,
425 lines => \@lines,
426 );
427 }
428#endif
429 }
430 return covered;
431}
ExPolygons _anchor_regions
Definition BridgeDetector.hpp:77
double angle
Definition BridgeDetector.hpp:42
int64_t coord_t
Definition libslic3r.h:48
T rad2deg(T angle)
Definition Geometry.hpp:310
Slic3r::Polygons union_(const Slic3r::Polygons &subject)
Definition ClipperUtils.cpp:801
Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip)
Definition ClipperUtils.hpp:571
Slic3r::Polygons intersection(const Slic3r::Polygon &subject, const Slic3r::Polygon &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:769
static void get_trapezoids2(const ExPolygon &expoly, Polygons *polygons)
Definition BridgeDetector.cpp:247
std::vector< Polygon, PointsAllocator< Polygon > > Polygons
Definition Polygon.hpp:22
Polygons to_polygons(const ExPolygon &src)
Definition ExPolygon.hpp:307
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:605
void get_trapezoids3_half(const ExPolygon &expoly, Polygons *polygons, float spacing)
Definition BridgeDetector.cpp:288
void polygons_rotate(Polygons &polys, double angle)
Definition Polygon.hpp:169

References _anchor_regions, angle, Slic3r::BoundingBoxBase< PointType, APointsType >::center(), expolygons, Slic3r::get_trapezoids2(), Slic3r::get_trapezoids3_half(), Slic3r::intersection(), Slic3r::intersection_ln(), Slic3r::offset_ex(), PI, Slic3r::polygons_rotate(), Slic3r::Geometry::rad2deg(), spacing, Slic3r::to_polygons(), and Slic3r::union_().

Referenced by detect_angle(), and Slic3r::PerimeterGenerator::process_no_bridge().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ detect_angle()

bool Slic3r::BridgeDetector::detect_angle ( double bridge_direction_override = 0.)
87{
88 if (this->_edges.empty() || this->_anchor_regions.empty())
89 // The bridging region is completely in the air, there are no anchors available at the layer below.
90 return false;
91
92 std::vector<BridgeDirection> candidates;
93 if (bridge_direction_override == 0.) {
94 std::vector<double> angles = bridge_direction_candidates();
95 candidates.reserve(angles.size());
96 for (size_t i = 0; i < angles.size(); ++ i)
97 candidates.emplace_back(BridgeDirection(angles[i]));
98 } else
99 candidates.emplace_back(BridgeDirection(bridge_direction_override));
100
101 /* Outset the bridge expolygon by half the amount we used for detecting anchors;
102 we'll use this one to clip our test lines and be sure that their endpoints
103 are inside the anchors and not on their contours leading to false negatives. */
104 Polygons clip_area = offset(this->expolygons, 0.5f * float(this->spacing));
105
106 /* we'll now try several directions using a rudimentary visibility check:
107 bridge in several directions and then sum the length of lines having both
108 endpoints within anchors */
109
110 bool have_coverage = false;
111 for (size_t i_angle = 0; i_angle < candidates.size(); ++ i_angle)
112 {
113 const double angle = candidates[i_angle].angle;
114
115 Lines lines;
116 {
117 // Get an oriented bounding box around _anchor_regions.
118 BoundingBox bbox = get_extents_rotated(this->_anchor_regions, - angle);
119 // Cover the region with line segments.
120 lines.reserve((bbox.max(1) - bbox.min(1) + this->spacing) / this->spacing);
121 double s = sin(angle);
122 double c = cos(angle);
123 //FIXME Vojtech: The lines shall be spaced half the line width from the edge, but then
124 // some of the test cases fail. Need to adjust the test cases then?
125// for (coord_t y = bbox.min(1) + this->spacing / 2; y <= bbox.max(1); y += this->spacing)
126 for (coord_t y = bbox.min(1); y <= bbox.max(1); y += this->spacing)
127 lines.push_back(Line(
128 Point((coord_t)round(c * bbox.min(0) - s * y), (coord_t)round(c * y + s * bbox.min(0))),
129 Point((coord_t)round(c * bbox.max(0) - s * y), (coord_t)round(c * y + s * bbox.max(0)))));
130 }
131
132 double total_length = 0;
133 double max_length = 0;
134 {
135 Lines clipped_lines = intersection_ln(lines, clip_area);
136 size_t archored_line_num = 0;
137 for (size_t i = 0; i < clipped_lines.size(); ++i) {
138 const Line &line = clipped_lines[i];
139 if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) {
140 // This line could be anchored.
141 double len = line.length();
142 total_length += len;
143 max_length = std::max(max_length, len);
144 archored_line_num++;
145 }
146 }
147 if (clipped_lines.size() > 0 && archored_line_num > 0) {
148 candidates[i_angle].archored_percent = (double)archored_line_num / (double)clipped_lines.size();
149 }
150 }
151 if (total_length == 0.)
152 continue;
153
154 have_coverage = true;
155 // Sum length of bridged lines.
156 candidates[i_angle].coverage = total_length;
157 /* The following produces more correct results in some cases and more broken in others.
158 TODO: investigate, as it looks more reliable than line clipping. */
159 // $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
160 // max length of bridged lines
161 candidates[i_angle].max_length = max_length;
162 }
163
164 // if no direction produced coverage, then there's no bridge direction
165 if (! have_coverage)
166 return false;
167
168 // sort directions by coverage - most coverage first
169 std::sort(candidates.begin(), candidates.end());
170
171 // if any other direction is within extrusion width of coverage, prefer it if shorter
172 // TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred?
173 size_t i_best = 0;
174// for (size_t i = 1; i < candidates.size() && abs(candidates[i_best].archored_percent - candidates[i].archored_percent) < EPSILON; ++ i)
175 for (size_t i = 1; i < candidates.size() && candidates[i_best].coverage - candidates[i].coverage < this->spacing; ++ i)
176 if (candidates[i].max_length < candidates[i_best].max_length)
177 i_best = i;
178
179 this->angle = candidates[i_best].angle;
180 if (this->angle >= PI)
181 this->angle -= PI;
182
183 #ifdef SLIC3R_DEBUG
184 printf(" Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle));
185 #endif
186
187 return true;
188}
std::vector< double > bridge_direction_candidates() const
Definition BridgeDetector.cpp:190
Polygons coverage(double angle=-1, bool precise=true) const
Definition BridgeDetector.cpp:335
Kernel::Point_3 Point
Definition MeshRepair.cpp:81
bool expolygons_contain(ExPolygons &expolys, const Point &pt, bool border_result=true)
Definition ExPolygon.hpp:469
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, JoinType joinType, double miterLimit)
Definition ClipperUtils.cpp:582
double total_length(const Polygons &polylines)
Definition Polygon.hpp:127
BoundingBox get_extents_rotated(const ExPolygon &expolygon, double angle)
Definition ExPolygon.cpp:426
Definition BridgeDetector.hpp:58

References _anchor_regions, _edges, Slic3r::Line::a, angle, Slic3r::Line::b, bridge_direction_candidates(), coverage(), expolygons, Slic3r::expolygons_contain(), Slic3r::get_extents_rotated(), Slic3r::intersection_ln(), Slic3r::Line::length(), Slic3r::BoundingBoxBase< PointType, APointsType >::max, Slic3r::BoundingBoxBase< PointType, APointsType >::min, Slic3r::offset(), PI, Slic3r::Geometry::rad2deg(), spacing, and Slic3r::total_length().

Referenced by Slic3r::PerimeterGenerator::process_no_bridge().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ initialize()

void Slic3r::BridgeDetector::initialize ( )
private
47{
48 // 5 degrees stepping
49 this->resolution = PI/36.0;
50 // output angle not known
51 this->angle = -1.;
52
53 // Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
54 Polygons grown = offset(this->expolygons, float(this->spacing));
55
56 // Detect possible anchoring edges of this bridging region.
57 // Detect what edges lie on lower slices by turning bridge contour and holes
58 // into polylines and then clipping them with each lower slice's contour.
59 // Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
60 Polygons contours;
61 contours.reserve(this->lower_slices.size());
62 for (const ExPolygon &expoly : this->lower_slices)
63 contours.push_back(expoly.contour);
64 this->_edges = intersection_pl(to_polylines(grown), contours);
65
66 #ifdef SLIC3R_DEBUG
67 printf(" bridge has %zu support(s)\n", this->_edges.size());
68 #endif
69
70 // detect anchors as intersection between our bridge expolygon and the lower slices
71 // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges
73
74 /*
75 if (0) {
76 require "Slic3r/SVG.pm";
77 Slic3r::SVG::output("bridge.svg",
78 expolygons => [ $self->expolygon ],
79 red_expolygons => $self->lower_slices,
80 polylines => $self->_edges,
81 );
82 }
83 */
84}
Polylines to_polylines(const ExPolygon &src)
Definition ExPolygon.hpp:235
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, ApplySafetyOffset do_safety_offset)
Definition ClipperUtils.cpp:878
Slic3r::Polygons union_safety_offset(const Slic3r::Polygons &polygons)
Definition ClipperUtils.hpp:386
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygon &clip)
Definition ClipperUtils.cpp:1111

References _anchor_regions, _edges, angle, expolygons, Slic3r::intersection_ex(), Slic3r::intersection_pl(), lower_slices, Slic3r::offset(), PI, resolution, spacing, Slic3r::to_polylines(), and Slic3r::union_safety_offset().

Referenced by BridgeDetector(), and BridgeDetector().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ operator=()

BridgeDetector & Slic3r::BridgeDetector::operator= ( const BridgeDetector & )
private

References BridgeDetector().

Here is the call graph for this function:

◆ unsupported_edges() [1/2]

void Slic3r::BridgeDetector::unsupported_edges ( double angle,
Polylines * unsupported ) const
438{
439 if (angle == -1) angle = this->angle;
440 if (angle == -1) return;
441
442 Polygons grown_lower = offset(this->lower_slices, float(this->spacing));
443
444 for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {
445 // get unsupported bridge edges (both contour and holes)
446 Lines unsupported_lines = to_lines(diff_pl(to_polylines(*it_expoly), grown_lower));
447 /* Split into individual segments and filter out edges parallel to the bridging angle
448 TODO: angle tolerance should probably be based on segment length and flow width,
449 so that we build supports whenever there's a chance that at least one or two bridge
450 extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
451 direction might still benefit from anchors if long enough)
452 double angle_tolerance = PI / 180.0 * 5.0; */
453 for (const Line &line : unsupported_lines)
454 if (! Slic3r::Geometry::directions_parallel(line.direction(), angle)) {
455 unsupported->emplace_back(Polyline());
456 unsupported->back().points.emplace_back(line.a);
457 unsupported->back().points.emplace_back(line.b);
458 }
459 }
460
461 /*
462 if (0) {
463 require "Slic3r/SVG.pm";
464 Slic3r::SVG::output(
465 "unsupported_" . rad2deg($angle) . ".svg",
466 expolygons => [$self->expolygon],
467 green_expolygons => $self->_anchor_regions,
468 red_expolygons => union_ex($grown_lower),
469 no_arrows => 1,
470 polylines => \@bridge_edges,
471 red_polylines => $unsupported,
472 );
473 }
474 */
475}
Slic3r::Polylines diff_pl(const Slic3r::Polyline &subject, const Slic3r::Polygons &clip)
Definition ClipperUtils.cpp:1099

References angle, Slic3r::diff_pl(), Slic3r::Geometry::directions_parallel(), expolygons, lower_slices, Slic3r::offset(), spacing, Slic3r::to_lines(), and Slic3r::to_polylines().

Referenced by unsupported_edges().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ unsupported_edges() [2/2]

Polylines Slic3r::BridgeDetector::unsupported_edges ( double angle = -1) const
479{
480 Polylines pp;
481 this->unsupported_edges(angle, &pp);
482 return pp;
483}
void unsupported_edges(double angle, Polylines *unsupported) const
Definition BridgeDetector.cpp:437
std::vector< Polyline > Polylines
Definition Polyline.hpp:22

References angle, and unsupported_edges().

Here is the call graph for this function:

Member Data Documentation

◆ _anchor_regions

ExPolygons Slic3r::BridgeDetector::_anchor_regions
private

Referenced by coverage(), detect_angle(), and initialize().

◆ _edges

Polylines Slic3r::BridgeDetector::_edges
private

◆ angle

double Slic3r::BridgeDetector::angle

◆ expolygons

◆ expolygons_owned

ExPolygons Slic3r::BridgeDetector::expolygons_owned

Referenced by BridgeDetector().

◆ lower_slices

const ExPolygons& Slic3r::BridgeDetector::lower_slices

◆ resolution

double Slic3r::BridgeDetector::resolution

◆ spacing

coord_t Slic3r::BridgeDetector::spacing

The documentation for this class was generated from the following files: