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);
// detect what edges lie on lower slices by turning bridge contour and holes BridgeDetector::BridgeDetector(
// into polylines and then clipping them with each lower slice's contour const ExPolygons &_expolygons,
intersection(grown, this->lower_slices.contours(), &this->_edges); 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 possible anchoring edges of this bridging region.
// Detect what edges lie on lower slices by turning bridge contour and holes
// 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) {
Lines lines = p->lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line)
angles.push_back(line->direction()); 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
@ -108,90 +200,7 @@ BridgeDetector::detect_angle()
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
@ -200,56 +209,46 @@ 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;
// Get anchors, convert them to Polygons and rotate them.
Polygons anchors = to_polygons(this->_anchor_regions);
polygons_rotate(anchors, PI/2.0 - angle);
Polygons covered;
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly)
{
// Clone our expolygon and rotate it so that we work with vertical lines. // Clone our expolygon and rotate it so that we work with vertical lines.
ExPolygon expolygon = this->expolygon; ExPolygon expolygon = *it_expoly;
expolygon.rotate(PI/2.0 - angle, Point(0,0)); expolygon.rotate(PI/2.0 - angle);
/* 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 generate our trapezoids and be sure that their vertices 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. */ are inside the anchors and not on their contours leading to false negatives. */
ExPolygons grown; ExPolygons grown = offset_ex(expolygon, 0.5f * float(this->spacing));
offset(expolygon, &grown, this->extrusion_width/2.0);
// Compute trapezoids according to a vertical orientation // Compute trapezoids according to a vertical orientation
Polygons trapezoids; Polygons trapezoids;
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it)
it->get_trapezoids2(&trapezoids, PI/2.0); it->get_trapezoids2(&trapezoids, PI/2.0);
// get anchors, convert them to Polygons and rotate them too for (Polygons::iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
Polygons anchors; Lines supported = intersection(trapezoid->lines(), anchors);
for (ExPolygons::const_iterator anchor = this->_anchors.begin(); anchor != this->_anchors.end(); ++anchor) { size_t n_supported = 0;
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;
for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
Lines lines = trapezoid->lines();
Lines supported;
intersection(lines, anchors, &supported);
// not nice, we need a more robust non-numeric check // not nice, we need a more robust non-numeric check
for (size_t i = 0; i < supported.size(); ++i) { for (size_t i = 0; i < supported.size(); ++i)
if (supported[i].length() < this->extrusion_width) { if (supported[i].length() >= this->spacing)
supported.erase(supported.begin() + i); ++ n_supported;
i--; if (n_supported >= 2)
covered.push_back(STDMOVE(*trapezoid));
} }
} }
if (supported.size() >= 2) covered.push_back(*trapezoid); // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
} // instead of exact overlaps.
covered = union_(covered);
// merge trapezoids and rotate them back // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
Polygons _coverage; polygons_rotate(covered, -(PI/2.0 - angle));
union_(covered, &_coverage); intersection(covered, to_polygons(this->expolygons), coverage);
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
intersection(_coverage, this->expolygon, coverage);
/* /*
if (0) { if (0) {
@ -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,
); );
@ -285,28 +284,20 @@ 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;
{
Polygons pp = this->expolygon;
bridge_edges.insert(bridge_edges.end(), pp.begin(), pp.end()); // this uses split_at_first_point()
}
// get unsupported edges
Polygons grown_lower;
offset(this->lower_slices, &grown_lower, +this->extrusion_width);
Polylines _unsupported;
diff(bridge_edges, grown_lower, &_unsupported);
for (ExPolygons::const_iterator it_expoly = this->expolygons.begin(); it_expoly != this->expolygons.end(); ++ it_expoly) {
// get unsupported bridge edges (both contour and holes)
Polylines unuspported_polylines;
diff(to_polylines(*it_expoly), grown_lower, &unuspported_polylines);
Lines unsupported_lines = to_lines(unuspported_polylines);
/* Split into individual segments and filter out edges parallel to the bridging angle /* Split into individual segments and filter out edges parallel to the bridging angle
TODO: angle tolerance should probably be based on segment length and flow width, 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 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 extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
direction might still benefit from anchors if long enough) direction might still benefit from anchors if long enough)
double angle_tolerance = PI / 180.0 * 5.0; */ double angle_tolerance = PI / 180.0 * 5.0; */
for (Polylines::const_iterator polyline = _unsupported.begin(); polyline != _unsupported.end(); ++polyline) { for (Lines::const_iterator line = unsupported_lines.begin(); line != unsupported_lines.end(); ++line) {
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;
// 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; 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,24 +172,26 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
} }
#endif #endif
if (lower_layer != NULL)
{
// 2) Group the bridge surfaces by overlaps. // 2) Group the bridge surfaces by overlaps.
std::vector<size_t> bridge_group(bridges.surfaces.size(), (size_t)-1); std::vector<size_t> bridge_group(bridges.size(), (size_t)-1);
size_t n_groups = 0; size_t n_groups = 0;
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) { for (size_t i = 0; i < bridges.size(); ++ i) {
// A grup id for this bridge. // A grup id for this bridge.
size_t group_id = (bridge_group[i] == -1) ? (n_groups ++) : bridge_group[i]; size_t group_id = (bridge_group[i] == -1) ? (n_groups ++) : bridge_group[i];
bridge_group[i] = group_id; bridge_group[i] = group_id;
// For all possibly overlaping bridges: // For all possibly overlaping bridges:
for (size_t j = i + 1; j < bridges.surfaces.size(); ++ j) { for (size_t j = i + 1; j < bridges.size(); ++ j) {
if (! bridge_bboxes[i].overlap(bridge_bboxes[j])) if (! bridge_bboxes[i].overlap(bridge_bboxes[j]))
continue; continue;
if (! bridges.surfaces[i].expolygon.overlaps(bridges.surfaces[j].expolygon)) if (intersection(bridges_grown[i], bridges_grown[j], false).empty())
continue; continue;
// The two bridge regions intersect. Give them the same group id. // The two bridge regions intersect. Give them the same group id.
if (bridge_group[j] != -1) { if (bridge_group[j] != -1) {
// The j'th bridge has been merged with some other bridge before. // The j'th bridge has been merged with some other bridge before.
size_t group_id_new = bridge_group[j]; size_t group_id_new = bridge_group[j];
for (size_t k = 0; k < j; ++ k) for (size_t k = i; k < j; ++ k)
if (bridge_group[k] == group_id) if (bridge_group[k] == group_id)
bridge_group[k] = group_id_new; bridge_group[k] = group_id_new;
group_id = group_id_new; group_id = group_id_new;
@ -195,176 +200,135 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
} }
} }
// 3) Merge the groups with the same group id. // 3) Merge the groups with the same group id, detect bridges.
{ {
SurfaceCollection bridges_merged;
bridges_merged.surfaces.reserve(n_groups);
for (size_t group_id = 0; group_id < n_groups; ++ group_id) { for (size_t group_id = 0; group_id < n_groups; ++ group_id) {
size_t n_bridges_merged = 0; size_t n_bridges_merged = 0;
size_t idx_last = (size_t)-1; size_t idx_last = (size_t)-1;
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) { for (size_t i = 0; i < bridges.size(); ++ i) {
if (bridge_group[i] == group_id) { if (bridge_group[i] == group_id) {
++ n_bridges_merged; ++ n_bridges_merged;
idx_last = i; idx_last = i;
} }
} }
if (n_bridges_merged == 1) if (n_bridges_merged == 0)
bridges_merged.surfaces.push_back(bridges.surfaces[idx_last]); // This group has no regions assigned as these were moved into another group.
else if (n_bridges_merged > 1) { continue;
Slic3r::Polygons polygons; // Collect the initial ungrown regions and the grown polygons.
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) { 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]);
} }
ExPolygons expp; // detect bridge direction before merging grown surfaces otherwise adjacent bridges
// without safety offset, artifacts are generated (GH #2494) // would get merged into a single one while they need different directions
union_(polygons, &expp, true); // also, supply the original expolygon instead of the grown one, because in case
Surface &surface0 = bridges.surfaces[idx_last]; // of very thin (but still working) anchors, the grown expolygon would go beyond them
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
{
static int iRun = 0;
bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun ++), true);
}
#endif
// 4) Detect bridge directions.
if (lower_layer != NULL) {
for (size_t i = 0; i < bridges.surfaces.size(); ++ i) {
Surface &surface = bridges.surfaces[i];
/* 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( BridgeDetector bd(
surface.expolygon, initial,
lower_layer->slices, lower_layer->slices,
//FIXME parameters are not correct! //FIXME parameters are not correct!
// flow(FlowRole role, bool bridge = false, double width = -1) const; // flow(FlowRole role, bool bridge = false, double width = -1) const;
this->flow(frInfill, this->layer()->height, true).scaled_width() this->flow(frInfill, true, this->layer()->height).scaled_width()
); );
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id()); printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
#endif #endif
if (bd.detect_angle()) { if (bd.detect_angle()) {
surface.bridge_angle = bd.angle; bridges[idx_last].bridge_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());
} }
} }
// without safety offset, artifacts are generated (GH #2494)
surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]);
} }
} }
bottom.surfaces.insert(bottom.surfaces.end(), bridges.surfaces.begin(), bridges.surfaces.end());
#if 0
{
static int iRun = 0;
bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun ++), true);
}
#endif
}
#endif #endif
SurfaceCollection top; // Collect top surfaces and internal surfaces.
for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { // Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill.
if (surface->surface_type != stTop) continue; Surfaces top;
Surfaces internal;
// give priority to bottom surfaces Polygons fill_boundaries;
ExPolygons grown = diff_ex( // This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries!
offset(surface->expolygon, +margin),
(Polygons)bottom
);
for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
Surface s = *surface;
s.expolygon = *it;
top.surfaces.push_back(s);
}
}
/* if we're slicing with no infill, we can't extend external surfaces
over non-existent infill */
SurfaceCollection fill_boundaries;
if (this->region()->config.fill_density.value > 0) {
fill_boundaries = SurfaceCollection(surfaces);
} else {
for (Surfaces::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) {
if (it->surface_type != stInternal)
fill_boundaries.surfaces.push_back(*it);
}
}
// intersect the grown surfaces with the actual fill boundaries
SurfaceCollection new_surfaces;
{ {
// merge top and bottom in a single collection // bottom_polygons are used to trim inflated top surfaces.
SurfaceCollection tb = top; const Polygons bottom_polygons = to_polygons(bottom);
tb.surfaces.insert(tb.surfaces.end(), bottom.surfaces.begin(), bottom.surfaces.end()); fill_boundaries.reserve(number_polygons(surfaces));
bool has_infill = this->region()->config.fill_density.value > 0.;
// group surfaces for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
std::vector<SurfacesPtr> groups; if (surface->surface_type == stTop)
tb.group(&groups); // Collect the top surfaces, inflate them and trim them by the bottom surfaces.
// This gives the priority to bottom surfaces.
for (std::vector<SurfacesPtr>::const_iterator g = groups.begin(); g != groups.end(); ++g) { surfaces_append(
Polygons subject; top,
for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) { STDMOVE(diff_ex(offset(surface->expolygon, float(margin)), bottom_polygons)),
Polygons pp = **s; *surface); // template
subject.insert(subject.end(), pp.begin(), pp.end()); bool internal_surface = surface->surface_type != stTop && ! surface->is_bottom();
} if (has_infill || surface->surface_type != stInternal) {
if (internal_surface)
ExPolygons expp = intersection_ex( // Make a copy as the following line uses the move semantics.
subject, internal.push_back(*surface);
(Polygons)fill_boundaries, polygons_append(fill_boundaries, STDMOVE(surface->expolygon));
true // to ensure adjacent expolygons are unified } else if (internal_surface)
); internal.push_back(STDMOVE(*surface));
for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) {
Surface s = *g->front();
s.expolygon = *ex;
new_surfaces.surfaces.push_back(s);
} }
} }
Surfaces new_surfaces;
// Merge top and bottom in a single collection.
surfaces_append(top, STDMOVE(bottom));
// Intersect the grown surfaces with the actual fill boundaries.
for (size_t i = 0; i < top.size(); ++ i) {
Surface &s1 = top[i];
if (s1.empty())
continue;
Polygons polys;
polygons_append(polys, STDMOVE(s1));
for (size_t j = i + 1; j < top.size(); ++ j) {
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);
} }
/* subtract the new top surfaces from the other non-top surfaces and re-add them */ // Subtract the new top surfaces from the other non-top surfaces and re-add them.
{ Polygons new_polygons = to_polygons(new_surfaces);
SurfaceCollection other; for (size_t i = 0; i < internal.size(); ++ i) {
for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { Surface &s1 = internal[i];
if (s->surface_type != stTop && !s->is_bottom()) if (s1.empty())
other.surfaces.push_back(*s); continue;
Polygons polys;
polygons_append(polys, STDMOVE(s1));
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);
} }
// group surfaces this->fill_surfaces.surfaces = STDMOVE(new_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
%} %}
}; };