mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	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:
		
							parent
							
								
									5a81731577
								
							
						
					
					
						commit
						22ca927f12
					
				
					 7 changed files with 433 additions and 443 deletions
				
			
		| 
						 | 
				
			
			@ -73,7 +73,7 @@ use Slic3r::Test;
 | 
			
		|||
    );
 | 
			
		||||
    my $lower = [
 | 
			
		||||
        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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,37 +5,48 @@
 | 
			
		|||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
class BridgeDirectionComparator {
 | 
			
		||||
    public:
 | 
			
		||||
    std::map<double,double> dir_coverage;  // angle => score
 | 
			
		||||
    
 | 
			
		||||
    BridgeDirectionComparator(double _extrusion_width)
 | 
			
		||||
        : extrusion_width(_extrusion_width)
 | 
			
		||||
    {};
 | 
			
		||||
    
 | 
			
		||||
    // the best direction is the one causing most lines to be bridged (thus most coverage)
 | 
			
		||||
    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)
 | 
			
		||||
BridgeDetector::BridgeDetector(
 | 
			
		||||
    ExPolygon                   _expolygon,
 | 
			
		||||
    const ExPolygonCollection  &_lower_slices, 
 | 
			
		||||
    coord_t                     _spacing) :
 | 
			
		||||
    // The original infill polygon, not inflated.
 | 
			
		||||
    expolygons(expolygons_owned),
 | 
			
		||||
    // All surfaces of the object supporting this region.
 | 
			
		||||
    lower_slices(_lower_slices),
 | 
			
		||||
    spacing(_spacing)
 | 
			
		||||
{
 | 
			
		||||
    /*  outset our bridge by an arbitrary amout; we'll use this outer margin
 | 
			
		||||
        for detecting anchors */
 | 
			
		||||
    Polygons grown;
 | 
			
		||||
    offset((Polygons)this->expolygon, &grown, this->extrusion_width);
 | 
			
		||||
    this->expolygons_owned.push_back(std::move(_expolygon));
 | 
			
		||||
    initialize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
    // into polylines and then clipping them with each lower slice's contour
 | 
			
		||||
    intersection(grown, this->lower_slices.contours(), &this->_edges);
 | 
			
		||||
    // 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
 | 
			
		||||
    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
 | 
			
		||||
    // 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) {
 | 
			
		||||
| 
						 | 
				
			
			@ -60,18 +71,103 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle
 | 
			
		|||
bool
 | 
			
		||||
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;
 | 
			
		||||
        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. */
 | 
			
		||||
    Polygons clip_area;
 | 
			
		||||
    offset((const Slic3r::Polygons)this->expolygon, &clip_area, +this->extrusion_width/2);
 | 
			
		||||
    Polygons clip_area = offset(this->expolygons, 0.5f * float(this->spacing));
 | 
			
		||||
    
 | 
			
		||||
    /*  we'll now try several directions using a rudimentary visibility check:
 | 
			
		||||
        bridge in several directions and then sum the length of lines having both
 | 
			
		||||
        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
 | 
			
		||||
    std::vector<double> angles;
 | 
			
		||||
    for (int i = 0; i <= PI/this->resolution; ++i)
 | 
			
		||||
| 
						 | 
				
			
			@ -79,20 +175,16 @@ BridgeDetector::detect_angle()
 | 
			
		|||
    
 | 
			
		||||
    // we also test angles of each bridge contour
 | 
			
		||||
    {
 | 
			
		||||
        Polygons pp = this->expolygon;
 | 
			
		||||
        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)
 | 
			
		||||
                angles.push_back(line->direction());
 | 
			
		||||
        }
 | 
			
		||||
        Lines lines = to_lines(this->expolygons);
 | 
			
		||||
        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
 | 
			
		||||
        (this finds the optimal angle for C-shaped supports) */
 | 
			
		||||
    for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge) {
 | 
			
		||||
        if (edge->first_point().coincides_with(edge->last_point())) continue;
 | 
			
		||||
        angles.push_back(Line(edge->first_point(), edge->last_point()).direction());
 | 
			
		||||
    }
 | 
			
		||||
    for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge)
 | 
			
		||||
        if (! edge->first_point().coincides_with(edge->last_point()))
 | 
			
		||||
            angles.push_back(Line(edge->first_point(), edge->last_point()).direction());
 | 
			
		||||
    
 | 
			
		||||
    // remove duplicates
 | 
			
		||||
    double min_resolution = PI/180.0;  // 1 degree
 | 
			
		||||
| 
						 | 
				
			
			@ -107,91 +199,8 @@ BridgeDetector::detect_angle()
 | 
			
		|||
        in case they are parallel (PI, 0) */
 | 
			
		||||
    if (Slic3r::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
 | 
			
		||||
        angles.pop_back();
 | 
			
		||||
    
 | 
			
		||||
    BridgeDirectionComparator bdcomp(this->extrusion_width);
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
    return angles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
| 
						 | 
				
			
			@ -199,58 +208,48 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
 | 
			
		|||
{
 | 
			
		||||
    if (angle == -1) angle = this->angle;
 | 
			
		||||
    if (angle == -1) return;
 | 
			
		||||
    
 | 
			
		||||
    // Clone our expolygon and rotate it so that we work with vertical lines.
 | 
			
		||||
    ExPolygon expolygon = this->expolygon;
 | 
			
		||||
    expolygon.rotate(PI/2.0 - angle, Point(0,0));
 | 
			
		||||
    
 | 
			
		||||
    /*  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());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // 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 (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) {
 | 
			
		||||
        Lines lines = trapezoid->lines();
 | 
			
		||||
        Lines supported;
 | 
			
		||||
        intersection(lines, anchors, &supported);
 | 
			
		||||
    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.
 | 
			
		||||
        ExPolygon expolygon = *it_expoly;
 | 
			
		||||
        expolygon.rotate(PI/2.0 - angle);
 | 
			
		||||
        
 | 
			
		||||
        // not nice, we need a more robust non-numeric check
 | 
			
		||||
        for (size_t i = 0; i < supported.size(); ++i) {
 | 
			
		||||
            if (supported[i].length() < this->extrusion_width) {
 | 
			
		||||
                supported.erase(supported.begin() + i);
 | 
			
		||||
                i--;
 | 
			
		||||
            }
 | 
			
		||||
        /*  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_ex(expolygon, 0.5f * float(this->spacing));
 | 
			
		||||
        
 | 
			
		||||
        // 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
 | 
			
		||||
    Polygons _coverage;
 | 
			
		||||
    union_(covered, &_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);
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // Unite the trapezoids before rotation, as the rotation creates tiny gaps and intersections between the trapezoids
 | 
			
		||||
    // instead of exact overlaps.
 | 
			
		||||
    covered = union_(covered);
 | 
			
		||||
 | 
			
		||||
    // Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
 | 
			
		||||
    polygons_rotate(covered, -(PI/2.0 - angle));
 | 
			
		||||
    intersection(covered, to_polygons(this->expolygons), coverage);
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    if (0) {
 | 
			
		||||
        my @lines = map @{$_->lines}, @$trapezoids;
 | 
			
		||||
| 
						 | 
				
			
			@ -260,7 +259,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const
 | 
			
		|||
        Slic3r::SVG::output(
 | 
			
		||||
            "coverage_" . rad2deg($angle) . ".svg",
 | 
			
		||||
            expolygons          => [$self->expolygon],
 | 
			
		||||
            green_expolygons    => $self->_anchors,
 | 
			
		||||
            green_expolygons    => $self->_anchor_regions,
 | 
			
		||||
            red_expolygons      => $coverage,
 | 
			
		||||
            lines               => \@lines,
 | 
			
		||||
        );
 | 
			
		||||
| 
						 | 
				
			
			@ -284,29 +283,21 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
 | 
			
		|||
{
 | 
			
		||||
    if (angle == -1) angle = this->angle;
 | 
			
		||||
    if (angle == -1) return;
 | 
			
		||||
    
 | 
			
		||||
    // get bridge edges (both contour and holes)
 | 
			
		||||
    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);
 | 
			
		||||
    
 | 
			
		||||
    /*  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,
 | 
			
		||||
        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) {
 | 
			
		||||
 | 
			
		||||
    Polygons grown_lower = offset(this->lower_slices.expolygons, float(this->spacing));
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
            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 (Lines::const_iterator line = unsupported_lines.begin(); line != unsupported_lines.end(); ++line) {
 | 
			
		||||
            if (!Slic3r::Geometry::directions_parallel(line->direction(), angle))
 | 
			
		||||
                unsupported->push_back(*line);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -318,7 +309,7 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
 | 
			
		|||
        Slic3r::SVG::output(
 | 
			
		||||
            "unsupported_" . rad2deg($angle) . ".svg",
 | 
			
		||||
            expolygons          => [$self->expolygon],
 | 
			
		||||
            green_expolygons    => $self->_anchors,
 | 
			
		||||
            green_expolygons    => $self->_anchor_regions,
 | 
			
		||||
            red_expolygons      => union_ex($grown_lower),
 | 
			
		||||
            no_arrows           => 1,
 | 
			
		||||
            polylines           => \@bridge_edges,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,20 +8,29 @@
 | 
			
		|||
 | 
			
		||||
namespace Slic3r {
 | 
			
		||||
 | 
			
		||||
// The bridge detector optimizes a direction of bridges over a region or a set of regions.
 | 
			
		||||
// A bridge direction is considered optimal, if the length of the lines strang over the region is maximal.
 | 
			
		||||
// This is optimal if the bridge is supported in a single direction only, but
 | 
			
		||||
// it may not likely be optimal, if the bridge region is supported from all sides. Then an optimal 
 | 
			
		||||
// solution would find a direction with shortest bridges.
 | 
			
		||||
// The bridge orientation is measured CCW from the X axis.
 | 
			
		||||
class BridgeDetector {
 | 
			
		||||
public:
 | 
			
		||||
    // The non-grown hole.
 | 
			
		||||
    ExPolygon expolygon;
 | 
			
		||||
    // The non-grown holes.
 | 
			
		||||
    const ExPolygons            &expolygons;
 | 
			
		||||
    // In case the caller gaves us the input polygons by a value, make a copy.
 | 
			
		||||
    ExPolygons                   expolygons_owned;
 | 
			
		||||
    // Lower slices, all regions.
 | 
			
		||||
    ExPolygonCollection lower_slices;
 | 
			
		||||
    const ExPolygonCollection   &lower_slices;
 | 
			
		||||
    // Scaled extrusion width of the infill.
 | 
			
		||||
    double extrusion_width;
 | 
			
		||||
    coord_t                      spacing;
 | 
			
		||||
    // Angle resolution for the brute force search of the best bridging angle.
 | 
			
		||||
    double resolution;
 | 
			
		||||
    double                       resolution;
 | 
			
		||||
    // 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();
 | 
			
		||||
    void coverage(double angle, Polygons* coverage) const;
 | 
			
		||||
    Polygons coverage(double angle = -1) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -29,10 +38,27 @@ public:
 | 
			
		|||
    Polylines unsupported_edges(double angle = -1) const;
 | 
			
		||||
    
 | 
			
		||||
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.
 | 
			
		||||
    Polylines _edges;
 | 
			
		||||
    // Closed polygons representing the supporting areas.
 | 
			
		||||
    ExPolygons _anchors;
 | 
			
		||||
    ExPolygons _anchor_regions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -116,9 +116,9 @@ Polygons
 | 
			
		|||
ExPolygonCollection::contours() const
 | 
			
		||||
{
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
    return contours;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,17 @@
 | 
			
		|||
 | 
			
		||||
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.
 | 
			
		||||
// The Slic3r::Layer::Region at this point of time may contain
 | 
			
		||||
// 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
 | 
			
		||||
    // without any angle (shouldn't this logic be moved to process_external_surfaces()?)
 | 
			
		||||
    {
 | 
			
		||||
        SurfacesPtr surfaces_with_bridge_angle;
 | 
			
		||||
        surfaces_with_bridge_angle.reserve(layerm.fill_surfaces.surfaces.size());
 | 
			
		||||
        Polygons polygons_bridged;
 | 
			
		||||
        polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size());
 | 
			
		||||
        for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it)
 | 
			
		||||
            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 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
 | 
			
		||||
            // (we'll use them for comparing compatible groups)
 | 
			
		||||
            std::vector<char>   is_solid(groups.size(), false);
 | 
			
		||||
            std::vector<float>  fw(groups.size(), 0.f);
 | 
			
		||||
            std::vector<int>    pattern(groups.size(), -1);
 | 
			
		||||
            std::vector<SurfaceGroupAttrib> group_attrib(groups.size());
 | 
			
		||||
            for (size_t i = 0; i < groups.size(); ++ i) {
 | 
			
		||||
                // we can only merge solid non-bridge surfaces, so discard
 | 
			
		||||
                // non-solid surfaces
 | 
			
		||||
                const Surface &surface = *groups[i].front();
 | 
			
		||||
                if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) {
 | 
			
		||||
                    is_solid[i] = true;
 | 
			
		||||
                    fw[i] = (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].is_solid = true;
 | 
			
		||||
                    group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width;
 | 
			
		||||
                    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) {
 | 
			
		||||
                if (is_solid[i]) {
 | 
			
		||||
                    // find compatible groups and append them to this one
 | 
			
		||||
                    for (size_t j = i + 1; j < groups.size(); ++ j) {
 | 
			
		||||
                        if (is_solid[j] && fw[i] == fw[j] && pattern[i] == pattern[j]) {
 | 
			
		||||
                            // groups are compatible, merge them
 | 
			
		||||
                            groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end());
 | 
			
		||||
                            groups.erase(groups.begin() + j);
 | 
			
		||||
                            is_solid.erase(is_solid.begin() + j);
 | 
			
		||||
                            fw.erase(fw.begin() + j);
 | 
			
		||||
                            pattern.erase(pattern.begin() + j);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                if (! group_attrib[i].is_solid)
 | 
			
		||||
                    continue;
 | 
			
		||||
                for (size_t j = i + 1; j < groups.size();) {
 | 
			
		||||
                    if (group_attrib[i] == group_attrib[j]) {
 | 
			
		||||
                        // groups are compatible, merge them
 | 
			
		||||
                        groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end());
 | 
			
		||||
                        groups.erase(groups.begin() + j);
 | 
			
		||||
                        group_attrib.erase(group_attrib.begin() + j);
 | 
			
		||||
                    } else
 | 
			
		||||
                         ++ 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.
 | 
			
		||||
                Polygons union_p = union_(to_polygons(*it_group), true);
 | 
			
		||||
                // 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)
 | 
			
		||||
                    union_p = diff(union_p, to_polygons(surfaces_with_bridge_angle), true);
 | 
			
		||||
                if (! polygons_bridged.empty() && ! is_bridge)
 | 
			
		||||
                    union_p = diff(union_p, polygons_bridged, true);
 | 
			
		||||
                // subtract any other surface already processed
 | 
			
		||||
                //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice!
 | 
			
		||||
                ExPolygons union_expolys = diff_ex(union_p, to_polygons(surfaces), true);
 | 
			
		||||
                for (ExPolygons::const_iterator it_expoly = union_expolys.begin(); it_expoly != union_expolys.end(); ++ it_expoly)
 | 
			
		||||
                    surfaces.push_back(Surface(*it_group->front(), *it_expoly));
 | 
			
		||||
                // Using group.front() as a template.
 | 
			
		||||
                surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +160,7 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
 | 
			
		|||
        bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge();
 | 
			
		||||
        
 | 
			
		||||
        if (surface.is_solid()) {
 | 
			
		||||
            density = 100;
 | 
			
		||||
            density = 100.;
 | 
			
		||||
            fill_pattern = (surface.is_external() && ! is_bridge) ? 
 | 
			
		||||
                layerm.region()->config.external_fill_pattern.value :
 | 
			
		||||
                ipRectilinear;
 | 
			
		||||
| 
						 | 
				
			
			@ -224,7 +230,8 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
 | 
			
		|||
        // apply half spacing using this flow's own spacing and generate infill
 | 
			
		||||
        FillParams params;
 | 
			
		||||
        params.density = 0.01 * density;
 | 
			
		||||
        params.dont_adjust = true;
 | 
			
		||||
//        params.dont_adjust = true;
 | 
			
		||||
        params.dont_adjust = false;
 | 
			
		||||
        Polylines polylines = f->fill_surface(&surface, params);
 | 
			
		||||
        if (polylines.empty())
 | 
			
		||||
            continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -248,12 +255,9 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out)
 | 
			
		|||
            // Only concentric fills are not sorted.
 | 
			
		||||
            collection.no_sort = f->no_sort();
 | 
			
		||||
            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);
 | 
			
		||||
                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.
 | 
			
		||||
    // Why the paths are unpacked?
 | 
			
		||||
    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());
 | 
			
		||||
        out.entities.push_back(&collection);
 | 
			
		||||
        collection.entities.push_back((*thin_fill)->clone());
 | 
			
		||||
    #endif
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,7 +99,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
 | 
			
		|||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    SurfaceCollection bottom;
 | 
			
		||||
    Surfaces bottom;
 | 
			
		||||
    // For all stBottom && stBottomBridge surfaces:
 | 
			
		||||
    for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
 | 
			
		||||
        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 */
 | 
			
		||||
        double angle = -1;
 | 
			
		||||
        if (lower_layer != NULL) {
 | 
			
		||||
            ExPolygons expolygons;
 | 
			
		||||
            expolygons.push_back(surface->expolygon);
 | 
			
		||||
            BridgeDetector bd(
 | 
			
		||||
                surface->expolygon,
 | 
			
		||||
                expolygons,
 | 
			
		||||
                lower_layer->slices,
 | 
			
		||||
                this->flow(frInfill, this->layer()->height, true).scaled_width()
 | 
			
		||||
                this->flow(frInfill, true, this->layer()->height).scaled_width()
 | 
			
		||||
            );
 | 
			
		||||
            
 | 
			
		||||
            #ifdef SLIC3R_DEBUG
 | 
			
		||||
| 
						 | 
				
			
			@ -126,8 +128,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
 | 
			
		|||
                angle = bd.angle;
 | 
			
		||||
            
 | 
			
		||||
                if (this->layer()->object()->config.support_material) {
 | 
			
		||||
                    Polygons coverage = bd.coverage();
 | 
			
		||||
                    this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end());
 | 
			
		||||
                    polygons_append(this->bridged, bd.coverage());
 | 
			
		||||
                    this->unsupported_bridge_edges.append(bd.unsupported_edges()); 
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -137,28 +138,30 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
 | 
			
		|||
            Surface s       = *surface;
 | 
			
		||||
            s.expolygon     = *it;
 | 
			
		||||
            s.bridge_angle  = angle;
 | 
			
		||||
            bottom.surfaces.push_back(s);
 | 
			
		||||
            bottom.push_back(s);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
#else
 | 
			
		||||
    // 1) Collect bottom and bridge surfaces, each of them grown by a fixed 3mm offset
 | 
			
		||||
    // for better anchoring.
 | 
			
		||||
    SurfaceCollection bottom;
 | 
			
		||||
    SurfaceCollection bridges;
 | 
			
		||||
    std::vector<BoundingBox> bridge_bboxes;
 | 
			
		||||
    // Bottom surfaces, grown.
 | 
			
		||||
    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;
 | 
			
		||||
    // For all stBottom && stBottomBridge surfaces:
 | 
			
		||||
    for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
 | 
			
		||||
        if (!surface->is_bottom()) continue;
 | 
			
		||||
        // Grown by 3mm.
 | 
			
		||||
        ExPolygons grown = offset_ex(surface->expolygon, +margin);
 | 
			
		||||
        for (ExPolygons::const_iterator it = grown.begin(); it != grown.end(); ++it) {
 | 
			
		||||
            Surface s       = *surface;
 | 
			
		||||
            s.expolygon     = *it;
 | 
			
		||||
            if (surface->surface_type == stBottomBridge) {
 | 
			
		||||
                bridges.surfaces.push_back(s);
 | 
			
		||||
                bridge_bboxes.push_back(get_extents(s));
 | 
			
		||||
            } else
 | 
			
		||||
                bottom.surfaces.push_back(s);
 | 
			
		||||
        if (surface->surface_type == stBottom || lower_layer == NULL) {
 | 
			
		||||
            // Grown by 3mm.
 | 
			
		||||
            surfaces_append(bottom, offset_ex(surface->expolygon, float(margin)), *surface);
 | 
			
		||||
        } else if (surface->surface_type == stBottomBridge) {
 | 
			
		||||
            bridges.push_back(*surface);
 | 
			
		||||
            // Grown by 3mm.
 | 
			
		||||
            bridges_grown.push_back(offset(surface->expolygon, float(margin)));
 | 
			
		||||
            bridge_bboxes.push_back(get_extents(bridges_grown.back()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -169,202 +172,163 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer)
 | 
			
		|||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    // 2) Group the bridge surfaces by overlaps.
 | 
			
		||||
    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.
 | 
			
		||||
    if (lower_layer != NULL)
 | 
			
		||||
    {
 | 
			
		||||
        SurfaceCollection bridges_merged;
 | 
			
		||||
        bridges_merged.surfaces.reserve(n_groups);
 | 
			
		||||
        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.surfaces.size(); ++ i) {
 | 
			
		||||
                if (bridge_group[i] == group_id) {
 | 
			
		||||
                    ++ n_bridges_merged;
 | 
			
		||||
                    idx_last = i;
 | 
			
		||||
        // 2) Group the bridge surfaces by overlaps.
 | 
			
		||||
        std::vector<size_t> bridge_group(bridges.size(), (size_t)-1);
 | 
			
		||||
        size_t n_groups = 0; 
 | 
			
		||||
        for (size_t i = 0; i < bridges.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.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) {
 | 
			
		||||
                Slic3r::Polygons polygons;
 | 
			
		||||
                for (size_t i = 0; i < bridges.surfaces.size(); ++ i) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 3) Merge the groups with the same group id, detect bridges.
 | 
			
		||||
        {
 | 
			
		||||
            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)
 | 
			
		||||
                        continue;
 | 
			
		||||
                    const Surface &surface = bridges.surfaces[i];
 | 
			
		||||
                    polygons.push_back(surface.expolygon.contour);
 | 
			
		||||
                    for (size_t j = 0; j < surface.expolygon.holes.size(); ++ j)
 | 
			
		||||
                        polygons.push_back(surface.expolygon.holes[j]);
 | 
			
		||||
                    initial.push_back(STDMOVE(bridges[i].expolygon));
 | 
			
		||||
                    polygons_append(grown, bridges_grown[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(
 | 
			
		||||
                    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)
 | 
			
		||||
                union_(polygons, &expp, true);
 | 
			
		||||
                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);
 | 
			
		||||
                }
 | 
			
		||||
                surfaces_append(bottom, union_ex(grown, true), bridges[idx_last]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        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);
 | 
			
		||||
    #if 0
 | 
			
		||||
        {
 | 
			
		||||
            static int iRun = 0;
 | 
			
		||||
            bridges.export_to_svg(debug_out_path("bridges-after-grouping-%d.svg", iRun ++), true);
 | 
			
		||||
        }
 | 
			
		||||
    #endif
 | 
			
		||||
    }
 | 
			
		||||
#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(
 | 
			
		||||
                surface.expolygon,
 | 
			
		||||
                lower_layer->slices,
 | 
			
		||||
                //FIXME parameters are not correct!
 | 
			
		||||
                // flow(FlowRole role, bool bridge = false, double width = -1) const;
 | 
			
		||||
                this->flow(frInfill, this->layer()->height, true).scaled_width()
 | 
			
		||||
            );
 | 
			
		||||
            #ifdef SLIC3R_DEBUG
 | 
			
		||||
            printf("Processing bridge at layer " PRINTF_ZU ":\n", this->layer()->id());
 | 
			
		||||
            #endif            
 | 
			
		||||
            if (bd.detect_angle()) {
 | 
			
		||||
                surface.bridge_angle = bd.angle;
 | 
			
		||||
                if (this->layer()->object()->config.support_material) {
 | 
			
		||||
                    Polygons coverage = bd.coverage();
 | 
			
		||||
                    this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end());
 | 
			
		||||
                    this->unsupported_bridge_edges.append(bd.unsupported_edges()); 
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
    // Collect top surfaces and internal surfaces.
 | 
			
		||||
    // Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill.
 | 
			
		||||
    Surfaces        top;
 | 
			
		||||
    Surfaces        internal;
 | 
			
		||||
    Polygons        fill_boundaries;
 | 
			
		||||
    // This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries!
 | 
			
		||||
    {
 | 
			
		||||
        // bottom_polygons are used to trim inflated top surfaces.
 | 
			
		||||
        const Polygons bottom_polygons = to_polygons(bottom);
 | 
			
		||||
        fill_boundaries.reserve(number_polygons(surfaces));
 | 
			
		||||
        bool has_infill = this->region()->config.fill_density.value > 0.;
 | 
			
		||||
        for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) {
 | 
			
		||||
            if (surface->surface_type == stTop)
 | 
			
		||||
                // Collect the top surfaces, inflate them and trim them by the bottom surfaces.
 | 
			
		||||
                // This gives the priority to bottom surfaces.
 | 
			
		||||
                surfaces_append(
 | 
			
		||||
                    top,
 | 
			
		||||
                    STDMOVE(diff_ex(offset(surface->expolygon, float(margin)), bottom_polygons)),
 | 
			
		||||
                    *surface); // template
 | 
			
		||||
            bool internal_surface = surface->surface_type != stTop && ! surface->is_bottom();
 | 
			
		||||
            if (has_infill || surface->surface_type != stInternal) {
 | 
			
		||||
                if (internal_surface)
 | 
			
		||||
                    // Make a copy as the following line uses the move semantics.
 | 
			
		||||
                    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;
 | 
			
		||||
    for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) {
 | 
			
		||||
        if (surface->surface_type != stTop) continue;
 | 
			
		||||
        
 | 
			
		||||
        // give priority to bottom surfaces
 | 
			
		||||
        ExPolygons grown = diff_ex(
 | 
			
		||||
            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);
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /*  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);
 | 
			
		||||
    // Subtract the new top surfaces from the other non-top surfaces and re-add them.
 | 
			
		||||
    Polygons new_polygons = to_polygons(new_surfaces);
 | 
			
		||||
    for (size_t i = 0; i < internal.size(); ++ i) {
 | 
			
		||||
        Surface &s1 = internal[i];
 | 
			
		||||
        if (s1.empty())
 | 
			
		||||
            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);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // intersect the grown surfaces with the actual fill boundaries
 | 
			
		||||
    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;
 | 
			
		||||
    this->fill_surfaces.surfaces = STDMOVE(new_surfaces);
 | 
			
		||||
 | 
			
		||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
 | 
			
		||||
    export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-final");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,5 +29,15 @@ BridgeDetector::new(expolygon, lower_slices, extrusion_width)
 | 
			
		|||
    OUTPUT:
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
%}
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue