Reworked the bridge detector to allow searching a single bridging

direction over multiple regions. This allows a single bridge to be
drawn over holes, which are too close to each other to allow
for separate bridges.
Fixes Bridging-Angle not optimal
https://github.com/prusa3d/Slic3r/issues/12

Re-allowed adaptive infill line width for solid infills. The adaptive
infill line width works in some circumstances, see Issue #15,
but the original implementation often changed the line width too
aggressively. The current implementation limits the line width change
to 20%.
Fixes Gaps between infill and perimeter leads to errors in laydown on following layer
https://github.com/prusa3d/Slic3r/issues/15
This commit is contained in:
bubnikv 2016-11-08 09:59:25 +01:00
parent 5a81731577
commit 22ca927f12
7 changed files with 433 additions and 443 deletions

View File

@ -73,7 +73,7 @@ use Slic3r::Test;
); );
my $lower = [ my $lower = [
Slic3r::ExPolygon->new( Slic3r::ExPolygon->new(
Slic3r::Polygon->new_scale([10,10],[10,20],[20,20],[20,30],[0,30],[0,10]), Slic3r::Polygon->new_scale([10,10],[10,20],[20,20],[30,30],[0,30],[0,0]),
), ),
]; ];
$_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview $_->translate(scale 20, scale 20) for $bridge, @$lower; # avoid negative coordinates for easier SVG preview

View File

@ -5,37 +5,48 @@
namespace Slic3r { namespace Slic3r {
class BridgeDirectionComparator { BridgeDetector::BridgeDetector(
public: ExPolygon _expolygon,
std::map<double,double> dir_coverage; // angle => score const ExPolygonCollection &_lower_slices,
coord_t _spacing) :
BridgeDirectionComparator(double _extrusion_width) // The original infill polygon, not inflated.
: extrusion_width(_extrusion_width) expolygons(expolygons_owned),
{}; // All surfaces of the object supporting this region.
lower_slices(_lower_slices),
// the best direction is the one causing most lines to be bridged (thus most coverage) spacing(_spacing)
bool operator() (double a, double b) {
// Initial sort by coverage only - comparator must obey strict weak ordering
return (this->dir_coverage[a] > this->dir_coverage[b]);
};
private:
double extrusion_width;
};
BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices,
coord_t _extrusion_width)
: expolygon(_expolygon), lower_slices(_lower_slices), extrusion_width(_extrusion_width),
resolution(PI/36.0), angle(-1)
{ {
/* outset our bridge by an arbitrary amout; we'll use this outer margin this->expolygons_owned.push_back(std::move(_expolygon));
for detecting anchors */ initialize();
Polygons grown; }
offset((Polygons)this->expolygon, &grown, this->extrusion_width);
BridgeDetector::BridgeDetector(
const ExPolygons &_expolygons,
const ExPolygonCollection &_lower_slices,
coord_t _spacing) :
// The original infill polygon, not inflated.
expolygons(_expolygons),
// All surfaces of the object supporting this region.
lower_slices(_lower_slices),
spacing(_spacing)
{
initialize();
}
void BridgeDetector::initialize()
{
// 5 degrees stepping
this->resolution = PI/36.0;
// output angle not known
this->angle = -1.;
// Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
Polygons grown = offset(this->expolygons, float(this->spacing));
// detect what edges lie on lower slices by turning bridge contour and holes // Detect possible anchoring edges of this bridging region.
// into polylines and then clipping them with each lower slice's contour // Detect what edges lie on lower slices by turning bridge contour and holes
intersection(grown, this->lower_slices.contours(), &this->_edges); // into polylines and then clipping them with each lower slice's contour.
// Currently _edges are only used to set a candidate direction of the bridge (see bridge_direction_candidates()).
intersection(to_polylines(grown), this->lower_slices.contours(), &this->_edges);
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size()); printf(" bridge has " PRINTF_ZU " support(s)\n", this->_edges.size());
@ -43,7 +54,7 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle
// detect anchors as intersection between our bridge expolygon and the lower slices // detect anchors as intersection between our bridge expolygon and the lower slices
// safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges
intersection(grown, this->lower_slices, &this->_anchors, true); this->_anchor_regions = intersection_ex(grown, to_polygons(this->lower_slices.expolygons), true);
/* /*
if (0) { if (0) {
@ -60,18 +71,103 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle
bool bool
BridgeDetector::detect_angle() BridgeDetector::detect_angle()
{ {
if (this->_edges.empty() || this->_anchors.empty()) return false; if (this->_edges.empty() || this->_anchor_regions.empty())
// The bridging region is completely in the air, there are no anchors available at the layer below.
return false;
std::vector<BridgeDirection> candidates;
{
std::vector<double> angles = bridge_direction_candidates();
candidates.reserve(angles.size());
for (size_t i = 0; i < angles.size(); ++ i)
candidates.push_back(BridgeDirection(angles[i]));
}
/* Outset the bridge expolygon by half the amount we used for detecting anchors; /* Outset the bridge expolygon by half the amount we used for detecting anchors;
we'll use this one to clip our test lines and be sure that their endpoints we'll use this one to clip our test lines and be sure that their endpoints
are inside the anchors and not on their contours leading to false negatives. */ are inside the anchors and not on their contours leading to false negatives. */
Polygons clip_area; Polygons clip_area = offset(this->expolygons, 0.5f * float(this->spacing));
offset((const Slic3r::Polygons)this->expolygon, &clip_area, +this->extrusion_width/2);
/* we'll now try several directions using a rudimentary visibility check: /* we'll now try several directions using a rudimentary visibility check:
bridge in several directions and then sum the length of lines having both bridge in several directions and then sum the length of lines having both
endpoints within anchors */ endpoints within anchors */
bool have_coverage = false;
for (size_t i_angle = 0; i_angle < candidates.size(); ++ i_angle)
{
const double angle = candidates[i_angle].angle;
Lines lines;
{
// Get an oriented bounding box around _anchor_regions.
BoundingBox bbox = get_extents_rotated(this->_anchor_regions, - angle);
// Cover the region with line segments.
lines.reserve((bbox.max.y - bbox.min.y + this->spacing) / this->spacing);
double s = sin(angle);
double c = cos(angle);
//FIXME Vojtech: The lines shall be spaced half the line width from the edge, but then
// some of the test cases fail. Need to adjust the test cases then?
// for (coord_t y = bbox.min.y + this->spacing / 2; y <= bbox.max.y; y += this->spacing)
for (coord_t y = bbox.min.y; y <= bbox.max.y; y += this->spacing)
lines.push_back(Line(
Point((coord_t)round(c * bbox.min.x - s * y), (coord_t)round(c * y + s * bbox.min.x)),
Point((coord_t)round(c * bbox.max.x - s * y), (coord_t)round(c * y + s * bbox.max.x))));
}
double total_length = 0;
double max_length = 0;
{
Lines clipped_lines = intersection(lines, clip_area);
for (size_t i = 0; i < clipped_lines.size(); ++i) {
const Line &line = clipped_lines[i];
if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) {
// This line could be anchored.
double len = line.length();
total_length += len;
max_length = std::max(max_length, len);
}
}
}
if (total_length == 0.)
continue;
have_coverage = true;
// Sum length of bridged lines.
candidates[i_angle].coverage = total_length;
/* The following produces more correct results in some cases and more broken in others.
TODO: investigate, as it looks more reliable than line clipping. */
// $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
// max length of bridged lines
candidates[i_angle].max_length = max_length;
}
// if no direction produced coverage, then there's no bridge direction
if (! have_coverage)
return false;
// sort directions by coverage - most coverage first
std::sort(candidates.begin(), candidates.end());
// if any other direction is within extrusion width of coverage, prefer it if shorter
// TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred?
size_t i_best = 0;
for (size_t i = 1; i < candidates.size() && candidates[i_best].coverage - candidates[i].coverage < this->spacing; ++ i)
if (candidates[i].max_length < candidates[i_best].max_length)
i_best = i;
this->angle = candidates[i_best].angle;
if (this->angle >= PI)
this->angle -= PI;
#ifdef SLIC3R_DEBUG
printf(" Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle));
#endif
return true;
}
std::vector<double> BridgeDetector::bridge_direction_candidates() const
{
// we test angles according to configured resolution // we test angles according to configured resolution
std::vector<double> angles; std::vector<double> angles;
for (int i = 0; i <= PI/this->resolution; ++i) for (int i = 0; i <= PI/this->resolution; ++i)
@ -79,20 +175,16 @@ BridgeDetector::detect_angle()
// we also test angles of each bridge contour // we also test angles of each bridge contour
{ {
Polygons pp = this->expolygon; Lines lines = to_lines(this->expolygons);
for (Polygons::const_iterator p = pp.begin(); p != pp.end(); ++p) { for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
Lines lines = p->lines(); angles.push_back(line->direction());
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
angles.push_back(line->direction());
}
} }
/* we also test angles of each open supporting edge /* we also test angles of each open supporting edge
(this finds the optimal angle for C-shaped supports) */ (this finds the optimal angle for C-shaped supports) */
for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) { for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge)
if (edge->first_point().coincides_with(edge->last_point())) continue; if (! edge->first_point().coincides_with(edge->last_point()))
angles.push_back(Line(edge->first_point(), edge->last_point()).direction()); angles.push_back(Line(edge->first_point(), edge->last_point()).direction());
}
// remove duplicates // remove duplicates
double min_resolution = PI/180.0; // 1 degree double min_resolution = PI/180.0; // 1 degree
@ -107,91 +199,8 @@ BridgeDetector::detect_angle()
in case they are parallel (PI, 0) */ in case they are parallel (PI, 0) */
if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution)) if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
angles.pop_back(); angles.pop_back();
BridgeDirectionComparator bdcomp(this->extrusion_width); return angles;
std::map<double,double> dir_avg_length;
double line_increment = this->extrusion_width;
bool have_coverage = false;
for (std::vector<double>::const_iterator angle = angles.begin(); angle != angles.end(); ++angle) {
Polygons my_clip_area = clip_area;
ExPolygons my_anchors = this->_anchors;
// rotate everything - the center point doesn't matter
for (Polygons::iterator it = my_clip_area.begin(); it != my_clip_area.end(); ++it)
it->rotate(-*angle, Point(0,0));
for (ExPolygons::iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
it->rotate(-*angle, Point(0,0));
// generate lines in this direction
BoundingBox bb;
for (ExPolygons::const_iterator it = my_anchors.begin(); it != my_anchors.end(); ++it)
bb.merge((Points)*it);
Lines lines;
for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment)
lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y)));
Lines clipped_lines;
intersection(lines, my_clip_area, &clipped_lines);
// remove any line not having both endpoints within anchors
for (size_t i = 0; i < clipped_lines.size(); ++i) {
Line &line = clipped_lines[i];
if (!Slic3r::Geometry::contains(my_anchors, line.a)
|| !Slic3r::Geometry::contains(my_anchors, line.b)) {
clipped_lines.erase(clipped_lines.begin() + i);
--i;
}
}
std::vector<double> lengths;
double total_length = 0;
for (Lines::const_iterator line = clipped_lines.begin(); line != clipped_lines.end(); ++line) {
double len = line->length();
lengths.push_back(len);
total_length += len;
}
if (total_length) have_coverage = true;
// sum length of bridged lines
bdcomp.dir_coverage[*angle] = total_length;
/* The following produces more correct results in some cases and more broken in others.
TODO: investigate, as it looks more reliable than line clipping. */
// $directions_coverage{$angle} = sum(map $_->area, @{$self->coverage($angle)}) // 0;
// max length of bridged lines
dir_avg_length[*angle] = !lengths.empty()
? *std::max_element(lengths.begin(), lengths.end())
: 0;
}
// if no direction produced coverage, then there's no bridge direction
if (!have_coverage) return false;
// sort directions by coverage - most coverage first
std::sort(angles.begin(), angles.end(), bdcomp);
this->angle = angles.front();
// if any other direction is within extrusion width of coverage, prefer it if shorter
// TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred?
double most_coverage_angle = this->angle;
for (std::vector<double>::const_iterator angle = angles.begin() + 1;
angle != angles.end() && bdcomp.dir_coverage[most_coverage_angle] - bdcomp.dir_coverage[*angle] < this->extrusion_width;
++angle
) {
if (dir_avg_length[*angle] < dir_avg_length[this->angle]) {
this->angle = *angle;
}
}
if (this->angle >= PI) this->angle -= PI;
#ifdef SLIC3R_DEBUG
printf(" Optimal infill angle is %d degrees\n", (int)Slic3r::Geometry::rad2deg(this->angle));
#endif
return true;
} }
void void
@ -199,58 +208,48 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
{ {
if (angle == -1) angle = this->angle; if (angle == -1) angle = this->angle;
if (angle == -1) return; if (angle == -1) return;
// Clone our expolygon and rotate it so that we work with vertical lines. // Get anchors, convert them to Polygons and rotate them.
ExPolygon expolygon = this->expolygon; Polygons anchors = to_polygons(this->_anchor_regions);
expolygon.rotate(PI/2.0 - angle, Point(0,0)); polygons_rotate(anchors, PI/2.0 - angle);
/* Outset the bridge expolygon by half the amount we used for detecting anchors;
we'll use this one to generate our trapezoids and be sure that their vertices
are inside the anchors and not on their contours leading to false negatives. */
ExPolygons grown;
offset(expolygon, &grown, this->extrusion_width/2.0);
// Compute trapezoids according to a vertical orientation
Polygons trapezoids;
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
it->get_trapezoids2(&trapezoids, PI/2.0);
// get anchors, convert them to Polygons and rotate them too
Polygons anchors;
for (ExPolygons::const_iterator anchor = this->_anchors.begin(); anchor != this->_anchors.end(); ++anchor) {
Polygons pp = *anchor;
for (Polygons::iterator p = pp.begin(); p != pp.end(); ++p)
p->rotate(PI/2.0 - angle, Point(0,0));
anchors.insert(anchors.end(), pp.begin(), pp.end());
}
Polygons covered; Polygons covered;
for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
Lines lines = trapezoid->lines(); {
Lines supported; // Clone our expolygon and rotate it so that we work with vertical lines.
intersection(lines, anchors, &supported); ExPolygon expolygon = *it_expoly;
expolygon.rotate(PI/2.0 - angle);
// not nice, we need a more robust non-numeric check /* Outset the bridge expolygon by half the amount we used for detecting anchors;
for (size_t i = 0; i < supported.size(); ++i) { we'll use this one to generate our trapezoids and be sure that their vertices
if (supported[i].length() < this->extrusion_width) { are inside the anchors and not on their contours leading to false negatives. */
supported.erase(supported.begin() + i); ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
i--;
} // Compute trapezoids according to a vertical orientation
Polygons trapezoids;
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
it->get_trapezoids2(&trapezoids, PI/2.0);
for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
Lines supported = intersection(trapezoid->lines(), anchors);
size_t n_supported = 0;
// not nice, we need a more robust non-numeric check
for (size_t i = 0; i < supported.size(); ++i)
if (supported[i].length() >= this->spacing)
++ n_supported;
if (n_supported >= 2)
covered.push_back(STDMOVE(*trapezoid));
} }
if (supported.size() >= 2) covered.push_back(*trapezoid);
} }
// merge trapezoids and rotate them back // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
Polygons _coverage; // instead of exact overlaps.
union_(covered, &_coverage); covered = union_(covered);
for (Polygons::iterator p = _coverage.begin(); p != _coverage.end(); ++p)
p->rotate(-(PI/2.0 - angle), Point(0,0)); // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
polygons_rotate(covered, -(PI/2.0 - angle));
// intersect trapezoids with actual bridge area to remove extra margins intersection(covered, to_polygons(this->expolygons), coverage);
// and append it to result
intersection(_coverage, this->expolygon, coverage);
/* /*
if (0) { if (0) {
my @lines = map @{$_->lines}, @$trapezoids; my @lines = map @{$_->lines}, @$trapezoids;
@ -260,7 +259,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
Slic3r::SVG::output( Slic3r::SVG::output(
"coverage_" . rad2deg($angle) . ".svg", "coverage_" . rad2deg($angle) . ".svg",
expolygons => [$self->expolygon], expolygons => [$self->expolygon],
green_expolygons => $self->_anchors, green_expolygons => $self->_anchor_regions,
red_expolygons => $coverage, red_expolygons => $coverage,
lines => \@lines, lines => \@lines,
); );
@ -284,29 +283,21 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
{ {
if (angle == -1) angle = this->angle; if (angle == -1) angle = this->angle;
if (angle == -1) return; if (angle == -1) return;
// get bridge edges (both contour and holes) Polygons grown_lower = offset(this->lower_slices.expolygons, float(this->spacing));
Polylines bridge_edges;
{ for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {
Polygons pp = this->expolygon; // get unsupported bridge edges (both contour and holes)
bridge_edges.insert(bridge_edges.end(), pp.begin(), pp.end()); // this uses split_at_first_point() Polylines unuspported_polylines;
} diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines);
Lines unsupported_lines = to_lines(unuspported_polylines);
// get unsupported edges /* Split into individual segments and filter out edges parallel to the bridging angle
Polygons grown_lower; TODO: angle tolerance should probably be based on segment length and flow width,
offset(this->lower_slices, &grown_lower, +this->extrusion_width); so that we build supports whenever there's a chance that at least one or two bridge
Polylines _unsupported; extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
diff(bridge_edges, grown_lower, &_unsupported); direction might still benefit from anchors if long enough)
double angle_tolerance = PI / 180.0 * 5.0; */
/* Split into individual segments and filter out edges parallel to the bridging angle for (Lines::const_iterator line = unsupported_lines.begin(); line != unsupported_lines.end(); ++line) {
TODO: angle tolerance should probably be based on segment length and flow width,
so that we build supports whenever there's a chance that at least one or two bridge
extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
direction might still benefit from anchors if long enough)
double angle_tolerance = PI / 180.0 * 5.0; */
for (Polylines::const_iterator polyline = _unsupported.begin(); polyline != _unsupported.end(); ++polyline) {
Lines lines = polyline->lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
if (!Slic3r::Geometry::directions_parallel(line->direction(), angle)) if (!Slic3r::Geometry::directions_parallel(line->direction(), angle))
unsupported->push_back(*line); unsupported->push_back(*line);
} }
@ -318,7 +309,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
Slic3r::SVG::output( Slic3r::SVG::output(
"unsupported_" . rad2deg($angle) . ".svg", "unsupported_" . rad2deg($angle) . ".svg",
expolygons => [$self->expolygon], expolygons => [$self->expolygon],
green_expolygons => $self->_anchors, green_expolygons => $self->_anchor_regions,
red_expolygons => union_ex($grown_lower), red_expolygons => union_ex($grown_lower),
no_arrows => 1, no_arrows => 1,
polylines => \@bridge_edges, polylines => \@bridge_edges,

View File

@ -8,20 +8,29 @@
namespace Slic3r { 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 { class BridgeDetector {
public: public:
// The non-grown hole. // The non-grown holes.
ExPolygon expolygon; 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. // Lower slices, all regions.
ExPolygonCollection lower_slices; const ExPolygonCollection &lower_slices;
// Scaled extrusion width of the infill. // Scaled extrusion width of the infill.
double extrusion_width; coord_t spacing;
// Angle resolution for the brute force search of the best bridging angle. // Angle resolution for the brute force search of the best bridging angle.
double resolution; double resolution;
// The final optimal angle. // The final optimal angle.
double angle; double angle;
BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); BridgeDetector(ExPolygon _expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
BridgeDetector(const ExPolygons &_expolygons, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width);
bool detect_angle(); bool detect_angle();
void coverage(double angle, Polygons* coverage) const; void coverage(double angle, Polygons* coverage) const;
Polygons coverage(double angle = -1) const; Polygons coverage(double angle = -1) const;
@ -29,10 +38,27 @@ public:
Polylines unsupported_edges(double angle = -1) const; Polylines unsupported_edges(double angle = -1) const;
private: private:
void initialize();
struct BridgeDirection {
BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(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;
};
double angle;
double coverage;
double max_length;
};
// Get possible briging direction candidates.
std::vector<double> bridge_direction_candidates() const;
// Open lines representing the supporting edges. // Open lines representing the supporting edges.
Polylines _edges; Polylines _edges;
// Closed polygons representing the supporting areas. // Closed polygons representing the supporting areas.
ExPolygons _anchors; ExPolygons _anchor_regions;
}; };
} }

View File

@ -116,9 +116,9 @@ Polygons
ExPolygonCollection::contours() const ExPolygonCollection::contours() const
{ {
Polygons contours; Polygons contours;
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { contours.reserve(this->expolygons.size());
for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it)
contours.push_back(it->contour); contours.push_back(it->contour);
}
return contours; return contours;
} }

View File

@ -13,6 +13,17 @@
namespace Slic3r { namespace Slic3r {
struct SurfaceGroupAttrib
{
SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {}
bool operator==(const SurfaceGroupAttrib &other) const
{ return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; }
bool is_solid;
float fw;
// pattern is of type InfillPattern, -1 for an unset pattern.
int pattern;
};
// Generate infills for Slic3r::Layer::Region. // Generate infills for Slic3r::Layer::Region.
// The Slic3r::Layer::Region at this point of time may contain // The Slic3r::Layer::Region at this point of time may contain
// surfaces of various types (internal/bridge/top/bottom/solid). // surfaces of various types (internal/bridge/top/bottom/solid).
@ -34,11 +45,11 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// in case of bridge surfaces, the ones with defined angle will be attached to the ones // in case of bridge surfaces, the ones with defined angle will be attached to the ones
// without any angle (shouldn't this logic be moved to process_external_surfaces()?) // without any angle (shouldn't this logic be moved to process_external_surfaces()?)
{ {
SurfacesPtr surfaces_with_bridge_angle; Polygons polygons_bridged;
surfaces_with_bridge_angle.reserve(layerm.fill_surfaces.surfaces.size()); polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size());
for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it) for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it)
if (it->bridge_angle >= 0) if (it->bridge_angle >= 0)
surfaces_with_bridge_angle.push_back(&(*it)); polygons_append(polygons_bridged, *it);
// group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle) // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle)
// group is of type Slic3r::SurfaceCollection // group is of type Slic3r::SurfaceCollection
@ -50,33 +61,29 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
{ {
// cache flow widths and patterns used for all solid groups // cache flow widths and patterns used for all solid groups
// (we'll use them for comparing compatible groups) // (we'll use them for comparing compatible groups)
std::vector<char> is_solid(groups.size(), false); std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
std::vector<float> fw(groups.size(), 0.f);
std::vector<int> pattern(groups.size(), -1);
for (size_t i = 0; i < groups.size(); ++ i) { for (size_t i = 0; i < groups.size(); ++ i) {
// we can only merge solid non-bridge surfaces, so discard // we can only merge solid non-bridge surfaces, so discard
// non-solid surfaces // non-solid surfaces
const Surface &surface = *groups[i].front(); const Surface &surface = *groups[i].front();
if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
is_solid[i] = true; group_attrib[i].is_solid = true;
fw[i] = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
pattern[i] = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear; group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear;
} }
} }
// loop through solid groups // Loop through solid groups, find compatible groups and append them to this one.
for (size_t i = 0; i < groups.size(); ++ i) { for (size_t i = 0; i < groups.size(); ++ i) {
if (is_solid[i]) { if (! group_attrib[i].is_solid)
// find compatible groups and append them to this one continue;
for (size_t j = i + 1; j < groups.size(); ++ j) { for (size_t j = i + 1; j < groups.size();) {
if (is_solid[j] && fw[i] == fw[j] && pattern[i] == pattern[j]) { if (group_attrib[i] == group_attrib[j]) {
// groups are compatible, merge them // groups are compatible, merge them
groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end()); groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end());
groups.erase(groups.begin() + j); groups.erase(groups.begin() + j);
is_solid.erase(is_solid.begin() + j); group_attrib.erase(group_attrib.begin() + j);
fw.erase(fw.begin() + j); } else
pattern.erase(pattern.begin() + j); ++ j;
}
}
} }
} }
} }
@ -91,13 +98,12 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// Make a union of polygons defining the infiill regions of a group, use a safety offset. // Make a union of polygons defining the infiill regions of a group, use a safety offset.
Polygons union_p = union_(to_polygons(*it_group), true); Polygons union_p = union_(to_polygons(*it_group), true);
// Subtract surfaces having a defined bridge_angle from any other, use a safety offset. // Subtract surfaces having a defined bridge_angle from any other, use a safety offset.
if (! surfaces_with_bridge_angle.empty() && it_group->front()->bridge_angle < 0) if (! polygons_bridged.empty() && ! is_bridge)
union_p = diff(union_p, to_polygons(surfaces_with_bridge_angle), true); union_p = diff(union_p, polygons_bridged, true);
// subtract any other surface already processed // subtract any other surface already processed
//FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice! //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
ExPolygons union_expolys = diff_ex(union_p, to_polygons(surfaces), true); // Using group.front() as a template.
for (ExPolygons::const_iterator it_expoly = union_expolys.begin(); it_expoly != union_expolys.end(); ++ it_expoly) surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front());
surfaces.push_back(Surface(*it_group->front(), *it_expoly));
} }
} }
} }
@ -154,7 +160,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge(); bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
if (surface.is_solid()) { if (surface.is_solid()) {
density = 100; density = 100.;
fill_pattern = (surface.is_external() && ! is_bridge) ? fill_pattern = (surface.is_external() && ! is_bridge) ?
layerm.region()->config.external_fill_pattern.value : layerm.region()->config.external_fill_pattern.value :
ipRectilinear; ipRectilinear;
@ -224,7 +230,8 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// apply half spacing using this flow's own spacing and generate infill // apply half spacing using this flow's own spacing and generate infill
FillParams params; FillParams params;
params.density = 0.01 * density; params.density = 0.01 * density;
params.dont_adjust = true; // params.dont_adjust = true;
params.dont_adjust = false;
Polylines polylines = f->fill_surface(&surface, params); Polylines polylines = f->fill_surface(&surface, params);
if (polylines.empty()) if (polylines.empty())
continue; continue;
@ -248,12 +255,9 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// Only concentric fills are not sorted. // Only concentric fills are not sorted.
collection.no_sort = f->no_sort(); collection.no_sort = f->no_sort();
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
ExtrusionPath *path = new ExtrusionPath(role); ExtrusionPath *path = new ExtrusionPath(role, flow.mm3_per_mm(), flow.width, flow.height);
collection.entities.push_back(path); collection.entities.push_back(path);
path->polyline.points.swap(it->points); path->polyline.points.swap(it->points);
path->mm3_per_mm = flow.mm3_per_mm();
path->width = flow.width,
path->height = flow.height;
} }
} }
} }
@ -264,14 +268,9 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
// The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection. // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection.
// Why the paths are unpacked? // Why the paths are unpacked?
for (ExtrusionEntitiesPtr::iterator thin_fill = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) { for (ExtrusionEntitiesPtr::iterator thin_fill = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) {
#if 0
out.entities.push_back((*thin_fill)->clone());
assert(dynamic_cast<ExtrusionEntityCollection*>(out.entities.back()) != NULL);
#else
ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection());
out.entities.push_back(&collection); out.entities.push_back(&collection);
collection.entities.push_back((*thin_fill)->clone()); collection.entities.push_back((*thin_fill)->clone());
#endif
} }
} }

View File

@ -99,7 +99,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
#if 0 #if 0
SurfaceCollection bottom; Surfaces bottom;
// For all stBottom && stBottomBridge surfaces: // For all stBottom && stBottomBridge surfaces:
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
if (!surface->is_bottom()) continue; if (!surface->is_bottom()) continue;
@ -112,10 +112,12 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
of very thin (but still working) anchors, the grown expolygon would go beyond them */ of very thin (but still working) anchors, the grown expolygon would go beyond them */
double angle = -1; double angle = -1;
if (lower_layer != NULL) { if (lower_layer != NULL) {
ExPolygons expolygons;
expolygons.push_back(surface->expolygon);
BridgeDetector bd( BridgeDetector bd(
surface->expolygon, expolygons,
lower_layer->slices, lower_layer->slices,
this->flow(frInfill, this->layer()->height, true).scaled_width() this->flow(frInfill, true, this->layer()->height).scaled_width()
); );
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
@ -126,8 +128,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
angle = bd.angle; angle = bd.angle;
if (this->layer()->object()->config.support_material) { if (this->layer()->object()->config.support_material) {
Polygons coverage = bd.coverage(); polygons_append(this->bridged, bd.coverage());
this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end());
this->unsupported_bridge_edges.append(bd.unsupported_edges()); this->unsupported_bridge_edges.append(bd.unsupported_edges());
} }
} }
@ -137,28 +138,30 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
Surface s = *surface; Surface s = *surface;
s.expolygon = *it; s.expolygon = *it;
s.bridge_angle = angle; s.bridge_angle = angle;
bottom.surfaces.push_back(s); bottom.push_back(s);
} }
} }
#else #else
// 1) Collect bottom and bridge surfaces, each of them grown by a fixed 3mm offset // 1) Collect bottom and bridge surfaces, each of them grown by a fixed 3mm offset
// for better anchoring. // for better anchoring.
SurfaceCollection bottom; // Bottom surfaces, grown.
SurfaceCollection bridges; Surfaces bottom;
std::vector<BoundingBox> bridge_bboxes; // Bridge surfaces, initialy not grown.
Surfaces bridges;
// Bridge expolygons, grown, to be tested for intersection with other bridge regions.
std::vector<Polygons> bridges_grown;
// Bounding boxes of bridges_grown.
std::vector<BoundingBox> bridge_bboxes;
// For all stBottom && stBottomBridge surfaces: // For all stBottom && stBottomBridge surfaces:
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
if (!surface->is_bottom()) continue; if (surface->surface_type == stBottom || lower_layer == NULL) {
// Grown by 3mm. // Grown by 3mm.
ExPolygons grown = offset_ex(surface->expolygon, +margin); surfaces_append(bottom, offset_ex(surface->expolygon, float(margin)), *surface);
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) { } else if (surface->surface_type == stBottomBridge) {
Surface s = *surface; bridges.push_back(*surface);
s.expolygon = *it; // Grown by 3mm.
if (surface->surface_type == stBottomBridge) { bridges_grown.push_back(offset(surface->expolygon, float(margin)));
bridges.surfaces.push_back(s); bridge_bboxes.push_back(get_extents(bridges_grown.back()));
bridge_bboxes.push_back(get_extents(s));
} else
bottom.surfaces.push_back(s);
} }
} }
@ -169,202 +172,163 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
} }
#endif #endif
// 2) Group the bridge surfaces by overlaps. if (lower_layer != NULL)
std::vector<size_t> bridge_group(bridges.surfaces.size(), (size_t)-1);
size_t n_groups = 0;
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) {
// A grup id for this bridge.
size_t group_id = (bridge_group[i] == -1) ? (n_groups ++) : bridge_group[i];
bridge_group[i] = group_id;
// For all possibly overlaping bridges:
for (size_t j = i + 1; j < bridges.surfaces.size(); ++ j) {
if (! bridge_bboxes[i].overlap(bridge_bboxes[j]))
continue;
if (! bridges.surfaces[i].expolygon.overlaps(bridges.surfaces[j].expolygon))
continue;
// The two bridge regions intersect. Give them the same group id.
if (bridge_group[j] != -1) {
// The j'th bridge has been merged with some other bridge before.
size_t group_id_new = bridge_group[j];
for (size_t k = 0; k < j; ++ k)
if (bridge_group[k] == group_id)
bridge_group[k] = group_id_new;
group_id = group_id_new;
}
bridge_group[j] = group_id;
}
}
// 3) Merge the groups with the same group id.
{ {
SurfaceCollection bridges_merged; // 2) Group the bridge surfaces by overlaps.
bridges_merged.surfaces.reserve(n_groups); std::vector<size_t> bridge_group(bridges.size(), (size_t)-1);
for (size_t group_id = 0; group_id < n_groups; ++ group_id) { size_t n_groups = 0;
size_t n_bridges_merged = 0; for (size_t i = 0; i < bridges.size(); ++ i) {
size_t idx_last = (size_t)-1; // A grup id for this bridge.
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) { size_t group_id = (bridge_group[i] == -1) ? (n_groups ++) : bridge_group[i];
if (bridge_group[i] == group_id) { bridge_group[i] = group_id;
++ n_bridges_merged; // For all possibly overlaping bridges:
idx_last = i; for (size_t j = i + 1; j < bridges.size(); ++ j) {
if (! bridge_bboxes[i].overlap(bridge_bboxes[j]))
continue;
if (intersection(bridges_grown[i], bridges_grown[j], false).empty())
continue;
// The two bridge regions intersect. Give them the same group id.
if (bridge_group[j] != -1) {
// The j'th bridge has been merged with some other bridge before.
size_t group_id_new = bridge_group[j];
for (size_t k = i; k < j; ++ k)
if (bridge_group[k] == group_id)
bridge_group[k] = group_id_new;
group_id = group_id_new;
} }
bridge_group[j] = group_id;
} }
if (n_bridges_merged == 1) }
bridges_merged.surfaces.push_back(bridges.surfaces[idx_last]);
else if (n_bridges_merged > 1) { // 3) Merge the groups with the same group id, detect bridges.
Slic3r::Polygons polygons; {
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) { for (size_t group_id = 0; group_id < n_groups; ++ group_id) {
size_t n_bridges_merged = 0;
size_t idx_last = (size_t)-1;
for (size_t i = 0; i < bridges.size(); ++ i) {
if (bridge_group[i] == group_id) {
++ n_bridges_merged;
idx_last = i;
}
}
if (n_bridges_merged == 0)
// This group has no regions assigned as these were moved into another group.
continue;
// Collect the initial ungrown regions and the grown polygons.
ExPolygons initial;
Polygons grown;
for (size_t i = 0; i < bridges.size(); ++ i) {
if (bridge_group[i] != group_id) if (bridge_group[i] != group_id)
continue; continue;
const Surface &surface = bridges.surfaces[i]; initial.push_back(STDMOVE(bridges[i].expolygon));
polygons.push_back(surface.expolygon.contour); polygons_append(grown, bridges_grown[i]);
for (size_t j = 0; j < surface.expolygon.holes.size(); ++ j) }
polygons.push_back(surface.expolygon.holes[j]); // detect bridge direction before merging grown surfaces otherwise adjacent bridges
// would get merged into a single one while they need different directions
// also, supply the original expolygon instead of the grown one, because in case
// of very thin (but still working) anchors, the grown expolygon would go beyond them
BridgeDetector bd(
initial,
lower_layer->slices,
//FIXME parameters are not correct!
// flow(FlowRole role, bool bridge = false, double width = -1) const;
this->flow(frInfill, true, this->layer()->height).scaled_width()
);
#ifdef SLIC3R_DEBUG
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
#endif
if (bd.detect_angle()) {
bridges[idx_last].bridge_angle = bd.angle;
if (this->layer()->object()->config.support_material) {
polygons_append(this->bridged, bd.coverage());
this->unsupported_bridge_edges.append(bd.unsupported_edges());
}
} }
ExPolygons expp;
// without safety offset, artifacts are generated (GH #2494) // without safety offset, artifacts are generated (GH #2494)
union_(polygons, &expp, true); surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]);
Surface &surface0 = bridges.surfaces[idx_last];
surface0.expolygon.clear();
for (size_t i = 0; i < expp.size(); ++ i) {
surface0.expolygon = expp[i];
bridges_merged.surfaces.push_back(surface0);
}
} }
} }
std::swap(bridges_merged, bridges);
}
#if 0 #if 0
{ {
static int iRun = 0; static int iRun = 0;
bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun ++), true); bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun ++), true);
}
#endif
} }
#endif #endif
// 4) Detect bridge directions. // Collect top surfaces and internal surfaces.
if (lower_layer != NULL) { // Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill.
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) { Surfaces top;
Surface &surface = bridges.surfaces[i]; Surfaces internal;
/* detect bridge direction before merging grown surfaces otherwise adjacent bridges Polygons fill_boundaries;
would get merged into a single one while they need different directions // This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries!
also, supply the original expolygon instead of the grown one, because in case {
of very thin (but still working) anchors, the grown expolygon would go beyond them */ // bottom_polygons are used to trim inflated top surfaces.
BridgeDetector bd( const Polygons bottom_polygons = to_polygons(bottom);
surface.expolygon, fill_boundaries.reserve(number_polygons(surfaces));
lower_layer->slices, bool has_infill = this->region()->config.fill_density.value > 0.;
//FIXME parameters are not correct! for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
// flow(FlowRole role, bool bridge = false, double width = -1) const; if (surface->surface_type == stTop)
this->flow(frInfill, this->layer()->height, true).scaled_width() // Collect the top surfaces, inflate them and trim them by the bottom surfaces.
); // This gives the priority to bottom surfaces.
#ifdef SLIC3R_DEBUG surfaces_append(
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id()); top,
#endif STDMOVE(diff_ex(offset(surface->expolygon, float(margin)), bottom_polygons)),
if (bd.detect_angle()) { *surface); // template
surface.bridge_angle = bd.angle; bool internal_surface = surface->surface_type != stTop && ! surface->is_bottom();
if (this->layer()->object()->config.support_material) { if (has_infill || surface->surface_type != stInternal) {
Polygons coverage = bd.coverage(); if (internal_surface)
this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end()); // Make a copy as the following line uses the move semantics.
this->unsupported_bridge_edges.append(bd.unsupported_edges()); internal.push_back(*surface);
} polygons_append(fill_boundaries, STDMOVE(surface->expolygon));
} } else if (internal_surface)
internal.push_back(STDMOVE(*surface));
} }
} }
bottom.surfaces.insert(bottom.surfaces.end(), bridges.surfaces.begin(), bridges.surfaces.end());
#endif
SurfaceCollection top; Surfaces new_surfaces;
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
if (surface->surface_type != stTop) continue; // Merge top and bottom in a single collection.
surfaces_append(top, STDMOVE(bottom));
// give priority to bottom surfaces // Intersect the grown surfaces with the actual fill boundaries.
ExPolygons grown = diff_ex( for (size_t i = 0; i < top.size(); ++ i) {
offset(surface->expolygon, +margin), Surface &s1 = top[i];
(Polygons)bottom if (s1.empty())
); continue;
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) { Polygons polys;
Surface s = *surface; polygons_append(polys, STDMOVE(s1));
s.expolygon = *it; for (size_t j = i + 1; j < top.size(); ++ j) {
top.surfaces.push_back(s); Surface &s2 = top[j];
if (! s2.empty() && surfaces_could_merge(s1, s2))
polygons_append(polys, STDMOVE(s2));
} }
surfaces_append(
new_surfaces,
STDMOVE(intersection_ex(polys, fill_boundaries, true)),
s1);
} }
/* if we're slicing with no infill, we can't extend external surfaces // Subtract the new top surfaces from the other non-top surfaces and re-add them.
over non-existent infill */ Polygons new_polygons = to_polygons(new_surfaces);
SurfaceCollection fill_boundaries; for (size_t i = 0; i < internal.size(); ++ i) {
if (this->region()->config.fill_density.value > 0) { Surface &s1 = internal[i];
fill_boundaries = SurfaceCollection(surfaces); if (s1.empty())
} else { continue;
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) { Polygons polys;
if (it->surface_type != stInternal) polygons_append(polys, STDMOVE(s1));
fill_boundaries.surfaces.push_back(*it); for (size_t j = i + 1; j < internal.size(); ++ j) {
Surface &s2 = internal[j];
if (! s2.empty() && surfaces_could_merge(s1, s2))
polygons_append(polys, STDMOVE(s2));
} }
ExPolygons new_expolys = diff_ex(polys, new_polygons);
polygons_append(new_polygons, to_polygons(new_expolys));
surfaces_append(new_surfaces, STDMOVE(new_expolys), s1);
} }
// intersect the grown surfaces with the actual fill boundaries this->fill_surfaces.surfaces = STDMOVE(new_surfaces);
SurfaceCollection new_surfaces;
{
// merge top and bottom in a single collection
SurfaceCollection tb = top;
tb.surfaces.insert(tb.surfaces.end(), bottom.surfaces.begin(), bottom.surfaces.end());
// group surfaces
std::vector<SurfacesPtr> groups;
tb.group(&groups);
for (std::vector<SurfacesPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
Polygons subject;
for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) {
Polygons pp = **s;
subject.insert(subject.end(), pp.begin(), pp.end());
}
ExPolygons expp = intersection_ex(
subject,
(Polygons)fill_boundaries,
true // to ensure adjacent expolygons are unified
);
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = *g->front();
s.expolygon = *ex;
new_surfaces.surfaces.push_back(s);
}
}
}
/* subtract the new top surfaces from the other non-top surfaces and re-add them */
{
SurfaceCollection other;
for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) {
if (s->surface_type != stTop && !s->is_bottom())
other.surfaces.push_back(*s);
}
// group surfaces
std::vector<SurfacesPtr> groups;
other.group(&groups);
for (std::vector<SurfacesPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) {
Polygons subject;
for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) {
Polygons pp = **s;
subject.insert(subject.end(), pp.begin(), pp.end());
}
ExPolygons expp = diff_ex(
subject,
(Polygons)new_surfaces
);
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = *g->front();
s.expolygon = *ex;
new_surfaces.surfaces.push_back(s);
}
}
}
this->fill_surfaces = new_surfaces;
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING #ifdef SLIC3R_DEBUG_SLICE_PROCESSING
export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final"); export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final");

View File

@ -29,5 +29,15 @@ BridgeDetector::new(expolygon, lower_slices, extrusion_width)
OUTPUT: OUTPUT:
RETVAL RETVAL
BridgeDetector*
BridgeDetector::new_expolygons(expolygons, lower_slices, extrusion_width)
ExPolygonCollection* expolygons;
ExPolygonCollection* lower_slices;
long extrusion_width;
CODE:
RETVAL = new BridgeDetector(expolygons->expolygons, *lower_slices, extrusion_width);
OUTPUT:
RETVAL
%} %}
}; };