void
PolylineCollection::chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse) const
{
    Polylines my_paths = this->polylines;

    Points endpoints;  //里面存储所有polyline里面的第一个点和允许翻转时的最后一个点的坐标
    for (Polylines::const_iterator it = my_paths.begin(); it != my_paths.end(); ++it) {
        endpoints.push_back(it->first_point());
        if (no_reverse) {
            endpoints.push_back(it->first_point());
        } else {
            endpoints.push_back(it->last_point());
        }
    }

    while (!my_paths.empty()) {
        // find nearest point
        int start_index = start_near.nearest_point_index(endpoints);
        int path_index = start_index/2;
        if (start_index % 2 && !no_reverse) {
            my_paths.at(path_index).reverse();
        }
        retval->polylines.push_back(my_paths.at(path_index));
        my_paths.erase(my_paths.begin() + path_index);
        endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2);
        start_near = retval->polylines.back().last_point();
    }
}
ExtrusionEntityCollection
PerimeterGenerator::_fill_gaps(double min, double max, double w,
    const Polygons &gaps) const
{
    ExtrusionEntityCollection coll;
    
    min *= (1 - INSET_OVERLAP_TOLERANCE);
    
    ExPolygons curr = diff_ex(
        offset2(gaps, -min/2, +min/2),
        offset2(gaps, -max/2, +max/2),
        true
    );
    
    Polylines polylines;
    for (ExPolygons::const_iterator ex = curr.begin(); ex != curr.end(); ++ex)
        ex->medial_axis(max, min/2, &polylines);
    if (polylines.empty())
        return coll;
    
    #ifdef SLIC3R_DEBUG
    if (!curr.empty())
        printf("  %zu gaps filled with extrusion width = %f\n", curr.size(), w);
    #endif
    
    //my $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 0, $w);
    Flow flow(
        w, this->layer_height, this->solid_infill_flow.nozzle_diameter
    );
    
    double mm3_per_mm = flow.mm3_per_mm();
    
    for (Polylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) {
        ExtrusionPath path(erGapFill);
        path.polyline   = *p;
        path.mm3_per_mm = mm3_per_mm;
        path.width      = flow.width;
        path.height     = this->layer_height;
        
        if (p->is_valid() && p->first_point().coincides_with(p->last_point())) {
            // since medial_axis() now returns only Polyline objects, detect loops here
            ExtrusionLoop loop;
            loop.paths.push_back(path);
            coll.append(loop);
        } else {
            coll.append(path);
        }
    }
    
    return coll;
}
bool
BridgeDetector::detect_angle()
{
    if (this->_edges.empty() || this->_anchors.empty()) return false;

    /*  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(this->expolygon, &clip_area, +this->extrusion_width/2);

    /*  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 */

    // we test angles according to configured resolution
    std::vector<double> angles;
    for (int i = 0; i <= PI/this->resolution; ++i)
        angles.push_back(i * this->resolution);

    // 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());
        }
    }

    /*  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());
    }

    // remove duplicates
    double min_resolution = PI/180.0;  // 1 degree
    std::sort(angles.begin(), angles.end());
    for (size_t i = 1; i < angles.size(); ++i) {
        if (xd::Geometry::directions_parallel(angles[i], angles[i-1], min_resolution)) {
            angles.erase(angles.begin() + i);
            --i;
        }
    }
    /*  compare first value with last one and remove the greatest one (PI)
        in case they are parallel (PI, 0) */
    if (xd::Geometry::directions_parallel(angles.front(), angles.back(), min_resolution))
        angles.pop_back();

    BridgeDirectionComparator bdcomp(this->extrusion_width);
    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 (!xd::Geometry::contains(my_anchors, line.a)
                    || !xd::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
        bdcomp.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 score
    std::sort(angles.begin(), angles.end(), bdcomp);

    this->angle = angles.front();
    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;
}