mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	1) Implemented anchoring of infill lines to perimeters with length
limited anchors, while before a full perimeter segment was always taken if possible. 2) Adapted the line infills (grid, stars, triangles, cubic) to 1). This also solves a long standing issue of these infills producing anchors for each sweep direction independently, thus possibly overlapping and overextruding, which was quite detrimental in narrow areas. 3) Refactored cubic adaptive infill anchroing algorithm for performance and clarity.
This commit is contained in:
		
							parent
							
								
									414fdaefc5
								
							
						
					
					
						commit
						239d588c5d
					
				
					 12 changed files with 1200 additions and 626 deletions
				
			
		| 
						 | 
				
			
			@ -553,21 +553,15 @@ static void export_infill_lines_to_svg(const ExPolygon &expoly, const Polylines
 | 
			
		|||
}
 | 
			
		||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
 | 
			
		||||
 | 
			
		||||
static Matrix2d rotation_matrix_from_vector(const Point &vector)
 | 
			
		||||
{
 | 
			
		||||
    Matrix2d rotation;
 | 
			
		||||
    rotation.block<1, 2>(0, 0) = vector.cast<double>().normalized();
 | 
			
		||||
    rotation(1, 0)             = -rotation(0, 1);
 | 
			
		||||
    rotation(1, 1)             = rotation(0, 0);
 | 
			
		||||
    return rotation;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Representing a T-joint (in general case) between two infill lines
 | 
			
		||||
// (between one end point of intersect_pl/intersect_line and 
 | 
			
		||||
struct Intersection
 | 
			
		||||
{
 | 
			
		||||
    // Index of the closest line to intersect_line
 | 
			
		||||
    size_t    closest_line_idx;
 | 
			
		||||
    // Copy of closest line to intersect_point, used for storing original line in an unchanged state
 | 
			
		||||
    Line      closest_line;
 | 
			
		||||
 | 
			
		||||
    // Point for which is computed closest line (closest_line)
 | 
			
		||||
    Point     intersect_point;
 | 
			
		||||
    // Index of the polyline from which is computed closest_line
 | 
			
		||||
| 
						 | 
				
			
			@ -577,54 +571,53 @@ struct Intersection
 | 
			
		|||
    // The line for which is computed closest line from intersect_point to closest_line
 | 
			
		||||
    Line      intersect_line;
 | 
			
		||||
    // Indicate if intersect_point is the first or the last point of intersect_pl
 | 
			
		||||
    bool      forward;
 | 
			
		||||
    bool      front;
 | 
			
		||||
 | 
			
		||||
    // Indication if this intersection has been proceed
 | 
			
		||||
    bool      used = false;
 | 
			
		||||
 | 
			
		||||
    Intersection(const size_t closest_line_idx,
 | 
			
		||||
                 const Line  &closest_line,
 | 
			
		||||
                 const Point &intersect_point,
 | 
			
		||||
                 size_t       intersect_pl_idx,
 | 
			
		||||
                 Polyline    *intersect_pl,
 | 
			
		||||
                 const Line  &intersect_line,
 | 
			
		||||
                 bool         forward)
 | 
			
		||||
        : closest_line_idx(closest_line_idx)
 | 
			
		||||
        , closest_line(closest_line)
 | 
			
		||||
        , intersect_point(intersect_point)
 | 
			
		||||
        , intersect_pl_idx(intersect_pl_idx)
 | 
			
		||||
        , intersect_pl(intersect_pl)
 | 
			
		||||
        , intersect_line(intersect_line)
 | 
			
		||||
        , forward(forward)
 | 
			
		||||
    {}
 | 
			
		||||
    bool      fresh() const throw() { return ! used && ! intersect_pl->empty(); }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline Intersection *get_nearest_intersection(std::vector<std::pair<Intersection, double>> &intersect_line, const size_t first_idx)
 | 
			
		||||
static inline Intersection *get_nearest_intersection(std::vector<std::pair<Intersection*, double>> &intersect_line, const size_t first_idx)
 | 
			
		||||
{
 | 
			
		||||
    assert(intersect_line.size() >= 2);
 | 
			
		||||
    bool take_next = false;
 | 
			
		||||
    if (first_idx == 0)
 | 
			
		||||
        return &intersect_line[first_idx + 1].first;
 | 
			
		||||
    else if (first_idx == (intersect_line.size() - 1))
 | 
			
		||||
        return &intersect_line[first_idx - 1].first;
 | 
			
		||||
    else if ((intersect_line[first_idx].second - intersect_line[first_idx - 1].second) < (intersect_line[first_idx + 1].second - intersect_line[first_idx].second))
 | 
			
		||||
        return &intersect_line[first_idx - 1].first;
 | 
			
		||||
    else
 | 
			
		||||
        return &intersect_line[first_idx + 1].first;
 | 
			
		||||
        take_next = true;
 | 
			
		||||
    else if (first_idx + 1 == intersect_line.size())
 | 
			
		||||
        take_next = false;
 | 
			
		||||
    else {
 | 
			
		||||
        // Has both prev and next.
 | 
			
		||||
        const std::pair<Intersection*, double> &ithis = intersect_line[first_idx];
 | 
			
		||||
        const std::pair<Intersection*, double> &iprev = intersect_line[first_idx - 1];
 | 
			
		||||
        const std::pair<Intersection*, double> &inext = intersect_line[first_idx + 1];
 | 
			
		||||
        take_next = iprev.first->fresh() && inext.first->fresh() ?
 | 
			
		||||
            inext.second - ithis.second < ithis.second - iprev.second :
 | 
			
		||||
            inext.first->fresh();
 | 
			
		||||
    }
 | 
			
		||||
    return intersect_line[take_next ? first_idx + 1 : first_idx - 1].first;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a line based on line_to_offset translated it in the direction of the intersection line (intersection.intersect_line)
 | 
			
		||||
// Create a line representing the anchor aka hook extrusion based on line_to_offset 
 | 
			
		||||
// translated in the direction of the intersection line (intersection.intersect_line).
 | 
			
		||||
static Line create_offset_line(const Line &line_to_offset, const Intersection &intersection, const double scaled_spacing)
 | 
			
		||||
{
 | 
			
		||||
    Matrix2d rotation          = rotation_matrix_from_vector(line_to_offset.vector());
 | 
			
		||||
    Vec2d    offset_vector     = ((scaled_spacing / 2.) * line_to_offset.normal().cast<double>().normalized());
 | 
			
		||||
    Vec2d    offset_line_point = line_to_offset.a.cast<double>();
 | 
			
		||||
    Vec2d    furthest_point    = (intersection.intersect_point == intersection.intersect_line.a ? intersection.intersect_line.b : intersection.intersect_line.a).cast<double>();
 | 
			
		||||
    Vec2d        dir            = line_to_offset.vector().cast<double>().normalized();
 | 
			
		||||
    // 50% overlap of the extrusion lines to achieve strong bonding.
 | 
			
		||||
    Vec2d        offset_vector  = Vec2d(- dir.y(), dir.x()) * (scaled_spacing / 2.);
 | 
			
		||||
    const Point &furthest_point = (intersection.intersect_point == intersection.intersect_line.a ? intersection.intersect_line.b : intersection.intersect_line.a);
 | 
			
		||||
 | 
			
		||||
    if ((rotation * furthest_point).y() >= (rotation * offset_line_point).y()) offset_vector *= -1;
 | 
			
		||||
    // Move inside.
 | 
			
		||||
    if (offset_vector.dot((furthest_point - intersection.intersect_point).cast<double>()) < 0.)
 | 
			
		||||
        offset_vector *= -1.;
 | 
			
		||||
 | 
			
		||||
    Line  offset_line    = line_to_offset;
 | 
			
		||||
    offset_line.translate(offset_vector.x(), offset_vector.y());
 | 
			
		||||
    // Extend the line by small value to guarantee a collision with adjacent lines
 | 
			
		||||
    // Extend the line by a small value to guarantee a collision with adjacent lines
 | 
			
		||||
    offset_line.extend(coord_t(scale_(1.)));
 | 
			
		||||
    //FIXME scaled_spacing * tan(PI/6)
 | 
			
		||||
//    offset_line.extend(coord_t(scaled_spacing * 0.577));
 | 
			
		||||
    return offset_line;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -637,26 +630,29 @@ using rtree_point_t   = bgm::point<float, 2, boost::geometry::cs::cartesian>;
 | 
			
		|||
using rtree_segment_t = bgm::segment<rtree_point_t>;
 | 
			
		||||
using rtree_t         = bgi::rtree<std::pair<rtree_segment_t, size_t>, bgi::rstar<16, 4>>;
 | 
			
		||||
 | 
			
		||||
static inline rtree_point_t mk_rtree_point(const Point &pt) {
 | 
			
		||||
    return rtree_point_t(float(pt.x()), float(pt.y()));
 | 
			
		||||
}
 | 
			
		||||
static inline rtree_segment_t mk_rtree_seg(const Point &a, const Point &b) {
 | 
			
		||||
    return { rtree_point_t(float(a.x()), float(a.y())), rtree_point_t(float(b.x()), float(b.y())) };
 | 
			
		||||
    return { mk_rtree_point(a), mk_rtree_point(b) };
 | 
			
		||||
}
 | 
			
		||||
static inline rtree_segment_t mk_rtree_seg(const Line &l) {
 | 
			
		||||
    return mk_rtree_seg(l.a, l.b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create a hook based on hook_line and append it to the begin or end of the polyline in the intersection
 | 
			
		||||
static void add_hook(const Intersection &intersection, const Line &hook_line, const double scaled_spacing, const int hook_length, const rtree_t &rtree)
 | 
			
		||||
static void add_hook(const Intersection &intersection, const double scaled_spacing, const int hook_length, const rtree_t &rtree)
 | 
			
		||||
{
 | 
			
		||||
    Vec2d  hook_vector_norm = hook_line.vector().cast<double>().normalized();
 | 
			
		||||
    Vector hook_vector      = (hook_length * hook_vector_norm).cast<coord_t>();
 | 
			
		||||
    Line   hook_line_offset = create_offset_line(hook_line, intersection, scaled_spacing);
 | 
			
		||||
 | 
			
		||||
    Point intersection_point;
 | 
			
		||||
    bool  intersection_found = intersection.intersect_line.intersection(hook_line_offset, &intersection_point);
 | 
			
		||||
    // Trim the hook start by the infill line it will connect to.
 | 
			
		||||
    Point hook_start;
 | 
			
		||||
    bool  intersection_found = intersection.intersect_line.intersection(
 | 
			
		||||
        create_offset_line(intersection.closest_line, intersection, scaled_spacing),
 | 
			
		||||
        &hook_start);
 | 
			
		||||
    assert(intersection_found);
 | 
			
		||||
 | 
			
		||||
    Line hook_forward(intersection_point, intersection_point + hook_vector);
 | 
			
		||||
    Line hook_backward(intersection_point, intersection_point - hook_vector);
 | 
			
		||||
    Vec2d   hook_vector_norm = intersection.closest_line.vector().cast<double>().normalized();
 | 
			
		||||
    Vector  hook_vector      = (hook_length * hook_vector_norm).cast<coord_t>();
 | 
			
		||||
    Line    hook_forward(hook_start, hook_start + hook_vector);
 | 
			
		||||
 | 
			
		||||
    auto filter_itself = [&intersection](const auto &item) {
 | 
			
		||||
        const rtree_segment_t &seg     = item.first;
 | 
			
		||||
| 
						 | 
				
			
			@ -666,51 +662,66 @@ static void add_hook(const Intersection &intersection, const Line &hook_line, co
 | 
			
		|||
    };
 | 
			
		||||
 | 
			
		||||
    std::vector<std::pair<rtree_segment_t, size_t>> hook_intersections;
 | 
			
		||||
    rtree.query(bgi::intersects(mk_rtree_seg(hook_forward)) && bgi::satisfies(filter_itself),
 | 
			
		||||
                std::back_inserter(hook_intersections));
 | 
			
		||||
    rtree.query(bgi::intersects(mk_rtree_seg(hook_forward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
 | 
			
		||||
 | 
			
		||||
    auto max_hook_length = [&hook_intersections, &hook_length](const Line &hook) {
 | 
			
		||||
        coord_t max_length = hook_length;
 | 
			
		||||
        for (const auto &hook_intersection : hook_intersections) {
 | 
			
		||||
            const rtree_segment_t &segment = hook_intersection.first;
 | 
			
		||||
            double                 dist    = Line::distance_to(hook.a, Point(bg::get<0, 0>(segment), bg::get<0, 1>(segment)),
 | 
			
		||||
                                            Point(bg::get<1, 0>(segment), bg::get<1, 1>(segment)));
 | 
			
		||||
            max_length                     = std::min(coord_t(dist), max_length);
 | 
			
		||||
        }
 | 
			
		||||
        return max_length;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Line hook_final;
 | 
			
		||||
    Point hook_end;
 | 
			
		||||
    if (hook_intersections.empty()) {
 | 
			
		||||
        hook_final = std::move(hook_forward);
 | 
			
		||||
        // The hook is not limited by another infill line. Extrude it in its full length.
 | 
			
		||||
        hook_end = hook_forward.b;
 | 
			
		||||
    } else {
 | 
			
		||||
        // There is not enough space for the hook, try another direction
 | 
			
		||||
        coord_t hook_forward_max_length = max_hook_length(hook_forward);
 | 
			
		||||
 | 
			
		||||
        // Find closest intersection of a line segment starting with pt pointing in dir
 | 
			
		||||
        // with any of the hook_intersections, returns Euclidian distance.
 | 
			
		||||
        // dir is normalized.
 | 
			
		||||
        auto max_hook_length = [hook_length](const Vec2d &pt, const Vec2d &dir, const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections) {
 | 
			
		||||
            // No hook is longer than hook_length, there shouldn't be any intersection closer than that.
 | 
			
		||||
            auto max_length = double(hook_length);
 | 
			
		||||
            auto update_max_length = [&max_length](double d) {
 | 
			
		||||
                if (d > 0. && d < max_length)
 | 
			
		||||
                    max_length = d;
 | 
			
		||||
            };
 | 
			
		||||
            for (const auto &hook_intersection : hook_intersections) {
 | 
			
		||||
                const rtree_segment_t &segment = hook_intersection.first;
 | 
			
		||||
                // Segment start and end points.
 | 
			
		||||
                Vec2d pt2(bg::get<0, 0>(segment), bg::get<0, 1>(segment));
 | 
			
		||||
                Vec2d pt2b(bg::get<1, 0>(segment), bg::get<1, 1>(segment));
 | 
			
		||||
                // Segment vector.
 | 
			
		||||
                Vec2d dir2 = pt2b - pt2;
 | 
			
		||||
                // Find intersection of (pt, dir) with (pt2, dir2), where dir is normalized.
 | 
			
		||||
                double denom = cross2(dir, dir2);
 | 
			
		||||
                if (std::abs(denom) < EPSILON) {
 | 
			
		||||
                    update_max_length((pt2 - pt).dot(dir));
 | 
			
		||||
                    update_max_length((pt2b - pt).dot(dir));
 | 
			
		||||
                } else
 | 
			
		||||
                    update_max_length(cross2(pt2 - pt, dir2) / denom);
 | 
			
		||||
            }
 | 
			
		||||
            return max_length;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // There is not enough space for the full hook length, try the opposite direction.
 | 
			
		||||
        Vec2d  hook_startf             = hook_start.cast<double>();
 | 
			
		||||
        double hook_forward_max_length = max_hook_length(hook_startf, hook_vector_norm, hook_intersections);
 | 
			
		||||
        hook_intersections.clear();
 | 
			
		||||
        rtree.query(bgi::intersects(mk_rtree_seg(hook_backward)) && bgi::satisfies(filter_itself),
 | 
			
		||||
                    std::back_inserter(hook_intersections));
 | 
			
		||||
        Line hook_backward(hook_start, hook_start - hook_vector);
 | 
			
		||||
        rtree.query(bgi::intersects(mk_rtree_seg(hook_backward)) && bgi::satisfies(filter_itself), std::back_inserter(hook_intersections));
 | 
			
		||||
 | 
			
		||||
        if (hook_intersections.empty()) {
 | 
			
		||||
            hook_final = std::move(hook_backward);
 | 
			
		||||
          // The hook in the other direction is not limited by another infill line. Extrude it in its full length.
 | 
			
		||||
            hook_end = hook_backward.b;
 | 
			
		||||
        } else {
 | 
			
		||||
            // There is not enough space for hook in both directions, shrink the hook
 | 
			
		||||
            coord_t hook_backward_max_length = max_hook_length(hook_backward);
 | 
			
		||||
            if (hook_forward_max_length > hook_backward_max_length) {
 | 
			
		||||
                Vector hook_vector_reduced = (hook_forward_max_length * hook_vector_norm).cast<coord_t>();
 | 
			
		||||
                hook_final                 = Line(intersection_point, intersection_point + hook_vector_reduced);
 | 
			
		||||
            } else {
 | 
			
		||||
                Vector hook_vector_reduced = (hook_backward_max_length * hook_vector_norm).cast<coord_t>();
 | 
			
		||||
                hook_final                 = Line(intersection_point, intersection_point - hook_vector_reduced);
 | 
			
		||||
            }
 | 
			
		||||
            // There is not enough space for the full hook in both directions, take the longer one.
 | 
			
		||||
            double hook_backward_max_length = max_hook_length(hook_startf, - hook_vector_norm, hook_intersections);
 | 
			
		||||
            Vec2d hook_dir = (hook_forward_max_length > hook_backward_max_length ? hook_forward_max_length : - hook_backward_max_length) * hook_vector_norm;
 | 
			
		||||
            hook_end = hook_start + hook_dir.cast<coord_t>();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (intersection.forward) {
 | 
			
		||||
        intersection.intersect_pl->points.front() = hook_final.a;
 | 
			
		||||
        intersection.intersect_pl->points.emplace(intersection.intersect_pl->points.begin(), hook_final.b);
 | 
			
		||||
    if (intersection.front) {
 | 
			
		||||
        intersection.intersect_pl->points.front() = hook_start;
 | 
			
		||||
        intersection.intersect_pl->points.emplace(intersection.intersect_pl->points.begin(), hook_end);
 | 
			
		||||
    } else {
 | 
			
		||||
        intersection.intersect_pl->points.back() = hook_final.a;
 | 
			
		||||
        intersection.intersect_pl->points.emplace_back(hook_final.b);
 | 
			
		||||
        intersection.intersect_pl->points.back() = hook_start;
 | 
			
		||||
        intersection.intersect_pl->points.emplace_back(hook_end);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -719,6 +730,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
    rtree_t rtree;
 | 
			
		||||
    size_t  poly_idx = 0;
 | 
			
		||||
    for (const Polyline &poly : lines) {
 | 
			
		||||
        assert(poly.points.size() == 2);
 | 
			
		||||
        rtree.insert(std::make_pair(mk_rtree_seg(poly.points.front(), poly.points.back()), poly_idx++));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -731,24 +743,28 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
            Polyline &line = lines[line_idx];
 | 
			
		||||
            // Lines shorter than spacing are skipped because it is needed to shrink a line by the value of spacing.
 | 
			
		||||
            // A shorter line than spacing could produce a degenerate polyline.
 | 
			
		||||
            if (line.length() <= (scaled_spacing + SCALED_EPSILON)) continue;
 | 
			
		||||
            //FIXME we should rather remove such short infill lines earlier!
 | 
			
		||||
            if (line.length() <= (scaled_spacing + SCALED_EPSILON))
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            Point                                           front_point = line.points.front();
 | 
			
		||||
            Point                                           back_point  = line.points.back();
 | 
			
		||||
            const Point &front_point = line.points.front();
 | 
			
		||||
            const Point &back_point  = line.points.back();
 | 
			
		||||
 | 
			
		||||
            auto filter_itself = [line_idx](const auto &item) { return item.second != line_idx; };
 | 
			
		||||
 | 
			
		||||
            // Find the nearest line from the start point of the line.
 | 
			
		||||
            closest.clear();
 | 
			
		||||
            rtree.query(bgi::nearest(rtree_point_t(float(front_point.x()), float(front_point.y())), 1) && bgi::satisfies(filter_itself), std::back_inserter(closest));
 | 
			
		||||
            if (((Line) lines[closest[0].second]).distance_to(front_point) <= 1000)
 | 
			
		||||
                intersections.emplace_back(closest[0].second, (Line) lines[closest[0].second], front_point, line_idx, &line, (Line) line, true);
 | 
			
		||||
            rtree.query(bgi::nearest(mk_rtree_point(front_point), 1) && bgi::satisfies(filter_itself), std::back_inserter(closest));
 | 
			
		||||
            if (((Line) lines[closest.front().second]).distance_to(front_point) <= 1000)
 | 
			
		||||
                // T-joint of line's front point with the 'closest' line.
 | 
			
		||||
                intersections.push_back({ closest.front().second, (Line)lines[closest.front().second], front_point, line_idx, &line, (Line)line, true });
 | 
			
		||||
 | 
			
		||||
            // Find the nearest line from the end point of the line
 | 
			
		||||
            closest.clear();
 | 
			
		||||
            rtree.query(bgi::nearest(rtree_point_t(float(back_point.x()), float(back_point.y())), 1) && bgi::satisfies(filter_itself), std::back_inserter(closest));
 | 
			
		||||
            if (((Line) lines[closest[0].second]).distance_to(back_point) <= 1000)
 | 
			
		||||
                intersections.emplace_back(closest[0].second, (Line) lines[closest[0].second], back_point, line_idx, &line, (Line) line, false);
 | 
			
		||||
            rtree.query(bgi::nearest(mk_rtree_point(back_point), 1) && bgi::satisfies(filter_itself), std::back_inserter(closest));
 | 
			
		||||
            if (((Line) lines[closest.front().second]).distance_to(back_point) <= 1000)
 | 
			
		||||
                // T-joint of line's back point with the 'closest' line.
 | 
			
		||||
                intersections.push_back({ closest.front().second, (Line)lines[closest.front().second], back_point, line_idx, &line, (Line)line, false });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -758,7 +774,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
    std::vector<size_t> merged_with(lines.size());
 | 
			
		||||
    std::iota(merged_with.begin(), merged_with.end(), 0);
 | 
			
		||||
 | 
			
		||||
    // Appends the boundary polygon with all holes to rtree for detection if hooks not crossing the boundary
 | 
			
		||||
    // Appends the boundary polygon with all holes to rtree for detection to check whether hooks are not crossing the boundary
 | 
			
		||||
    {
 | 
			
		||||
        Point prev = boundary.contour.points.back();
 | 
			
		||||
        for (const Point &point : boundary.contour.points) {
 | 
			
		||||
| 
						 | 
				
			
			@ -788,107 +804,97 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
 | 
			
		||||
        intersection.intersect_pl = &lines[intersection.intersect_pl_idx];
 | 
			
		||||
        // After polylines are merged, it is necessary to update "forward" based on if intersect_point is the first or the last point of intersect_pl.
 | 
			
		||||
        if (!intersection.used && !intersection.intersect_pl->points.empty())
 | 
			
		||||
            intersection.forward = (intersection.intersect_pl->points.front() == intersection.intersect_point);
 | 
			
		||||
        if (intersection.fresh())
 | 
			
		||||
            intersection.front = intersection.intersect_pl->points.front() == intersection.intersect_point;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (size_t min_idx = 0; min_idx < intersections.size(); ++min_idx) {
 | 
			
		||||
        std::vector<std::pair<Intersection, double>> intersect_line;
 | 
			
		||||
        Matrix2d                                     rotation = rotation_matrix_from_vector(intersections[min_idx].closest_line.vector());
 | 
			
		||||
        intersect_line.emplace_back(intersections[min_idx], (rotation * intersections[min_idx].intersect_point.cast<double>()).x());
 | 
			
		||||
        // All the nearest points on the same line are projected on this line. Because of it, it can easily find the nearest point
 | 
			
		||||
        for (size_t max_idx = min_idx + 1; max_idx < intersections.size(); ++max_idx) {
 | 
			
		||||
            if (intersections[min_idx].closest_line_idx != intersections[max_idx].closest_line_idx) break;
 | 
			
		||||
 | 
			
		||||
            intersect_line.emplace_back(intersections[max_idx], (rotation * intersections[max_idx].intersect_point.cast<double>()).x());
 | 
			
		||||
    // Keep intersect_line outside the loop, so it does not get reallocated.
 | 
			
		||||
    std::vector<std::pair<Intersection*, double>> intersect_line;
 | 
			
		||||
    for (size_t min_idx = 0; min_idx < intersections.size();) {
 | 
			
		||||
        const Vec2d line_dir = intersections[min_idx].closest_line.vector().cast<double>();
 | 
			
		||||
        intersect_line.clear();
 | 
			
		||||
        // All the nearest points (T-joints) ending at the same line are projected onto this line. Because of it, it can easily find the nearest point.
 | 
			
		||||
        {
 | 
			
		||||
            const Point &p0 = intersections[min_idx].intersect_point;
 | 
			
		||||
            size_t max_idx = min_idx + 1;
 | 
			
		||||
            intersect_line.emplace_back(&intersections[min_idx], 0.);
 | 
			
		||||
            for (; max_idx < intersections.size() && intersections[min_idx].closest_line_idx == intersections[max_idx].closest_line_idx; ++max_idx)
 | 
			
		||||
                intersect_line.emplace_back(&intersections[max_idx], line_dir.dot((intersections[max_idx].intersect_point - p0).cast<double>()));
 | 
			
		||||
            min_idx = max_idx;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert(!intersect_line.empty());
 | 
			
		||||
        if (intersect_line.size() <= 1) {
 | 
			
		||||
            // On the adjacent line is only one intersection
 | 
			
		||||
            Intersection &first_i = intersect_line.front().first;
 | 
			
		||||
            if (first_i.used || first_i.intersect_pl->points.empty()) continue;
 | 
			
		||||
 | 
			
		||||
            add_hook(first_i, first_i.closest_line, scale_(spacing), hook_length, rtree);
 | 
			
		||||
            first_i.used = true;
 | 
			
		||||
        if (intersect_line.size() == 1) {
 | 
			
		||||
            // Simple case: The current intersection is the only one touching its adjacent line.
 | 
			
		||||
            Intersection &first_i = *intersect_line.front().first;
 | 
			
		||||
            if (first_i.fresh()) {
 | 
			
		||||
                // Try to connect left or right. If not enough space for hook_length, take the longer side.
 | 
			
		||||
                add_hook(first_i, scale_(spacing), hook_length, rtree);
 | 
			
		||||
                first_i.used = true;
 | 
			
		||||
            }
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        assert(intersect_line.size() >= 2);
 | 
			
		||||
        assert(intersect_line.size() > 1);
 | 
			
		||||
        // Sort the intersections along line_dir.
 | 
			
		||||
        std::sort(intersect_line.begin(), intersect_line.end(), [](const auto &i1, const auto &i2) { return i1.second < i2.second; });
 | 
			
		||||
        for (size_t first_idx = 0; first_idx < intersect_line.size(); ++first_idx) {
 | 
			
		||||
            Intersection &first_i   = intersect_line[first_idx].first;
 | 
			
		||||
            Intersection &nearest_i = *get_nearest_intersection(intersect_line, first_idx);
 | 
			
		||||
        for (size_t first_idx = 0; first_idx < intersect_line.size(); ++ first_idx) {
 | 
			
		||||
            Intersection &first_i = *intersect_line[first_idx].first;
 | 
			
		||||
            if (! first_i.fresh())
 | 
			
		||||
                // The intersection has been processed, or the polyline has been merged to another polyline.
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            // Get the previous or next intersection on the same line, pick the closer one.
 | 
			
		||||
            Intersection &nearest_i = *get_nearest_intersection(intersect_line, first_idx);
 | 
			
		||||
            update_merged_polyline(first_i);
 | 
			
		||||
            update_merged_polyline(nearest_i);
 | 
			
		||||
 | 
			
		||||
            // The intersection has been processed, or the polyline has been merge to another polyline.
 | 
			
		||||
            if (first_i.used || first_i.intersect_pl->points.empty()) continue;
 | 
			
		||||
 | 
			
		||||
            // A line between two intersections points
 | 
			
		||||
            Line   intersection_line(first_i.intersect_point, nearest_i.intersect_point);
 | 
			
		||||
            Line   offset_line              = create_offset_line(intersection_line, first_i, scale_(spacing));
 | 
			
		||||
            double intersection_line_length = intersection_line.length();
 | 
			
		||||
 | 
			
		||||
            Line offset_line = create_offset_line(Line(first_i.intersect_point, nearest_i.intersect_point), first_i, scale_(spacing));
 | 
			
		||||
            // Check if both intersections lie on the offset_line and simultaneously get their points of intersecting.
 | 
			
		||||
            // These points are used as start and end of the hook
 | 
			
		||||
            Point first_i_point, nearest_i_point;
 | 
			
		||||
            if (first_i.intersect_line.intersection(offset_line, &first_i_point) &&
 | 
			
		||||
                nearest_i.intersect_line.intersection(offset_line, &nearest_i_point)) {
 | 
			
		||||
                // Both intersections are so close that their polylines can be connected
 | 
			
		||||
                if (!nearest_i.used && !nearest_i.intersect_pl->points.empty() && intersection_line_length <= 2 * hook_length) {
 | 
			
		||||
                if (nearest_i.fresh() && (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(3. * hook_length)) {
 | 
			
		||||
                    // Both intersections are so close that their polylines can be connected.
 | 
			
		||||
                    if (first_i.intersect_pl_idx == nearest_i.intersect_pl_idx) {
 | 
			
		||||
                        // Both intersections are on the same polyline
 | 
			
		||||
                        if (!first_i.forward) { std::swap(first_i_point, nearest_i_point); }
 | 
			
		||||
 | 
			
		||||
                        // Both intersections are on the same polyline, that means a loop is being closed.
 | 
			
		||||
                        if (! first_i.front)
 | 
			
		||||
                            std::swap(first_i_point, nearest_i_point);
 | 
			
		||||
                        first_i.intersect_pl->points.front() = first_i_point;
 | 
			
		||||
                        first_i.intersect_pl->points.back()  = nearest_i_point;
 | 
			
		||||
                        //FIXME trim the end of a closed loop a bit?
 | 
			
		||||
                        first_i.intersect_pl->points.emplace(first_i.intersect_pl->points.begin(), nearest_i_point);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Both intersections are on different polylines
 | 
			
		||||
                        Points merge_polyline_points;
 | 
			
		||||
                        size_t first_polyline_size     = first_i.intersect_pl->points.size();
 | 
			
		||||
                        size_t nearest_polyline_size   = nearest_i.intersect_pl->points.size();
 | 
			
		||||
                        merge_polyline_points.reserve(first_polyline_size + nearest_polyline_size);
 | 
			
		||||
 | 
			
		||||
                        if (first_i.forward) {
 | 
			
		||||
                            if (nearest_i.forward)
 | 
			
		||||
                                for (auto it = nearest_i.intersect_pl->points.rbegin(); it != nearest_i.intersect_pl->points.rend(); ++it)
 | 
			
		||||
                                    merge_polyline_points.emplace_back(*it);
 | 
			
		||||
                            else
 | 
			
		||||
                                for (const Point &point : nearest_i.intersect_pl->points)
 | 
			
		||||
                                    merge_polyline_points.emplace_back(point);
 | 
			
		||||
 | 
			
		||||
                            append(merge_polyline_points, std::move(first_i.intersect_pl->points));
 | 
			
		||||
                            merge_polyline_points[nearest_polyline_size - 1] = nearest_i_point;
 | 
			
		||||
                            merge_polyline_points[nearest_polyline_size]     = first_i_point;
 | 
			
		||||
                        Points &first_points  = first_i.intersect_pl->points;
 | 
			
		||||
                        Points &second_points = nearest_i.intersect_pl->points;
 | 
			
		||||
                        first_points.reserve(first_points.size() + second_points.size());
 | 
			
		||||
                        if (first_i.front)
 | 
			
		||||
                            std::reverse(first_points.begin(), first_points.end());
 | 
			
		||||
                        first_points.back() = first_i_point;
 | 
			
		||||
                        first_points.emplace_back(nearest_i_point);
 | 
			
		||||
                        if (nearest_i.front)
 | 
			
		||||
                            first_points.insert(first_points.end(), second_points.begin() + 1, second_points.end());
 | 
			
		||||
                        else
 | 
			
		||||
                            first_points.insert(first_points.end(), second_points.rbegin() + 1, second_points.rend());
 | 
			
		||||
                        // Keep the polyline at the lower index slot.
 | 
			
		||||
                        if (first_i.intersect_pl_idx < nearest_i.intersect_pl_idx) {
 | 
			
		||||
                            second_points.clear();
 | 
			
		||||
                            merged_with[nearest_i.intersect_pl_idx] = merged_with[first_i.intersect_pl_idx];
 | 
			
		||||
                        } else {
 | 
			
		||||
                            append(merge_polyline_points, std::move(first_i.intersect_pl->points));
 | 
			
		||||
                            if (nearest_i.forward)
 | 
			
		||||
                                for (const Point &point : nearest_i.intersect_pl->points)
 | 
			
		||||
                                    merge_polyline_points.emplace_back(point);
 | 
			
		||||
                            else
 | 
			
		||||
                                for (auto it = nearest_i.intersect_pl->points.rbegin(); it != nearest_i.intersect_pl->points.rend(); ++it)
 | 
			
		||||
                                    merge_polyline_points.emplace_back(*it);
 | 
			
		||||
 | 
			
		||||
                            merge_polyline_points[first_polyline_size - 1] = first_i_point;
 | 
			
		||||
                            merge_polyline_points[first_polyline_size]     = nearest_i_point;
 | 
			
		||||
                            second_points = std::move(first_points);
 | 
			
		||||
                            first_points.clear();
 | 
			
		||||
                            merged_with[first_i.intersect_pl_idx] = merged_with[nearest_i.intersect_pl_idx];
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        merged_with[nearest_i.intersect_pl_idx] = merged_with[first_i.intersect_pl_idx];
 | 
			
		||||
 | 
			
		||||
                        nearest_i.intersect_pl->points.clear();
 | 
			
		||||
                        first_i.intersect_pl->points = merge_polyline_points;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    first_i.used   = true;
 | 
			
		||||
                    nearest_i.used = true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    add_hook(first_i, first_i.closest_line, scale_(spacing), hook_length, rtree);
 | 
			
		||||
                    first_i.used = true;
 | 
			
		||||
                }
 | 
			
		||||
                } else
 | 
			
		||||
                    // Try to connect left or right. If not enough space for hook_length, take the longer side.
 | 
			
		||||
                    add_hook(first_i, scale_(spacing), hook_length, rtree);
 | 
			
		||||
                first_i.used = true;
 | 
			
		||||
            } else {
 | 
			
		||||
                // The first & last point should always be found.
 | 
			
		||||
                assert(false);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -124,7 +124,9 @@ protected:
 | 
			
		|||
    virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, double spacing, const FillParams ¶ms, const int hook_length = 0);
 | 
			
		||||
    static void connect_infill(Polylines &&infill_ordered, const ExPolygon &boundary, Polylines &polylines_out, const double spacing, const FillParams ¶ms, const int hook_length = 0);
 | 
			
		||||
    static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms, const int hook_length = 0);
 | 
			
		||||
    static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms, const int hook_length = 0);
 | 
			
		||||
 | 
			
		||||
    static coord_t  _adjust_solid_spacing(const coord_t width, const coord_t distance);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,12 +7,14 @@
 | 
			
		|||
#include <random>
 | 
			
		||||
 | 
			
		||||
#include <boost/container/small_vector.hpp>
 | 
			
		||||
#include <boost/log/trivial.hpp>
 | 
			
		||||
#include <boost/static_assert.hpp>
 | 
			
		||||
 | 
			
		||||
#include "../ClipperUtils.hpp"
 | 
			
		||||
#include "../ExPolygon.hpp"
 | 
			
		||||
#include "../Geometry.hpp"
 | 
			
		||||
#include "../Surface.hpp"
 | 
			
		||||
#include "../ShortestPath.hpp"
 | 
			
		||||
 | 
			
		||||
#include "FillRectilinear2.hpp"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +130,13 @@ struct SegmentIntersection
 | 
			
		|||
        return coord_t(p / int64_t(pos_q)); 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Left vertical line / contour intersection point.
 | 
			
		||||
    // null if next_on_contour_vertical.
 | 
			
		||||
    int32_t	prev_on_contour { 0 };
 | 
			
		||||
    // Right vertical line / contour intersection point.
 | 
			
		||||
    // If next_on_contour_vertical, then then next_on_contour contains next contour point on the same vertical line.
 | 
			
		||||
    int32_t	next_on_contour { 0 };
 | 
			
		||||
 | 
			
		||||
    // Kind of intersection. With the original contour, or with the inner offestted contour?
 | 
			
		||||
    // A vertical segment will be at least intersected by OUTER_LOW, OUTER_HIGH,
 | 
			
		||||
    // but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH,
 | 
			
		||||
| 
						 | 
				
			
			@ -141,13 +150,6 @@ struct SegmentIntersection
 | 
			
		|||
    };
 | 
			
		||||
    SegmentIntersectionType type { UNKNOWN };
 | 
			
		||||
 | 
			
		||||
    // Left vertical line / contour intersection point.
 | 
			
		||||
    // null if next_on_contour_vertical.
 | 
			
		||||
    int32_t	prev_on_contour { 0 };
 | 
			
		||||
    // Right vertical line / contour intersection point.
 | 
			
		||||
    // If next_on_contour_vertical, then then next_on_contour contains next contour point on the same vertical line.
 | 
			
		||||
    int32_t	next_on_contour { 0 };
 | 
			
		||||
 | 
			
		||||
    enum class LinkType : uint8_t {
 | 
			
		||||
    	// Horizontal link (left or right).
 | 
			
		||||
    	Horizontal,
 | 
			
		||||
| 
						 | 
				
			
			@ -383,30 +385,31 @@ public:
 | 
			
		|||
        const ExPolygon &expolygon,
 | 
			
		||||
        float   angle,
 | 
			
		||||
        coord_t aoffset1,
 | 
			
		||||
        coord_t aoffset2)
 | 
			
		||||
        // If the 2nd offset is zero, then it is ignored and only OUTER_LOW / OUTER_HIGH intersections are
 | 
			
		||||
        // populated into vertical intersection lines.
 | 
			
		||||
        coord_t aoffset2 = 0)
 | 
			
		||||
    {
 | 
			
		||||
        // Copy and rotate the source polygons.
 | 
			
		||||
        polygons_src = expolygon;
 | 
			
		||||
        polygons_src.contour.rotate(angle);
 | 
			
		||||
        for (Polygons::iterator it = polygons_src.holes.begin(); it != polygons_src.holes.end(); ++ it)
 | 
			
		||||
            it->rotate(angle);
 | 
			
		||||
        if (angle != 0.f) {
 | 
			
		||||
            polygons_src.contour.rotate(angle);
 | 
			
		||||
            for (Polygon &hole : polygons_src.holes)
 | 
			
		||||
                hole.rotate(angle);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        double mitterLimit = 3.;
 | 
			
		||||
        // for the infill pattern, don't cut the corners.
 | 
			
		||||
        // default miterLimt = 3
 | 
			
		||||
        //double mitterLimit = 10.;
 | 
			
		||||
        assert(aoffset1 < 0);
 | 
			
		||||
        assert(aoffset2 < 0);
 | 
			
		||||
        assert(aoffset2 < aoffset1);
 | 
			
		||||
        assert(aoffset2 <= 0);
 | 
			
		||||
        assert(aoffset2 == 0 || aoffset2 < aoffset1);
 | 
			
		||||
//        bool sticks_removed = 
 | 
			
		||||
        remove_sticks(polygons_src);
 | 
			
		||||
//        if (sticks_removed) printf("Sticks removed!\n");
 | 
			
		||||
        polygons_outer = offset(polygons_src, float(aoffset1),
 | 
			
		||||
            ClipperLib::jtMiter,
 | 
			
		||||
            mitterLimit);
 | 
			
		||||
        polygons_inner = offset(polygons_outer, float(aoffset2 - aoffset1),
 | 
			
		||||
            ClipperLib::jtMiter,
 | 
			
		||||
            mitterLimit);
 | 
			
		||||
//        if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!";
 | 
			
		||||
        polygons_outer = offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, mitterLimit);
 | 
			
		||||
        if (aoffset2 < 0)
 | 
			
		||||
            polygons_inner = offset(polygons_outer, float(aoffset2 - aoffset1), ClipperLib::jtMiter, mitterLimit);
 | 
			
		||||
		// Filter out contours with zero area or small area, contours with 2 points only.
 | 
			
		||||
        const double min_area_threshold = 0.01 * aoffset2 * aoffset2;
 | 
			
		||||
        remove_small(polygons_outer, min_area_threshold);
 | 
			
		||||
| 
						 | 
				
			
			@ -424,6 +427,18 @@ public:
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ExPolygonWithOffset(const ExPolygonWithOffset &rhs, float angle) : ExPolygonWithOffset(rhs) {
 | 
			
		||||
        if (angle != 0.f) {
 | 
			
		||||
            this->polygons_src.contour.rotate(angle);
 | 
			
		||||
            for (Polygon &hole : this->polygons_src.holes)
 | 
			
		||||
                hole.rotate(angle);
 | 
			
		||||
            for (Polygon &poly : this->polygons_outer)
 | 
			
		||||
                poly.rotate(angle);
 | 
			
		||||
            for (Polygon &poly : this->polygons_inner)
 | 
			
		||||
                poly.rotate(angle);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Any contour with offset1
 | 
			
		||||
    bool             is_contour_outer(size_t idx) const { return idx < n_contours_outer; }
 | 
			
		||||
    // Any contour with offset2
 | 
			
		||||
| 
						 | 
				
			
			@ -2644,7 +2659,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
 | 
			
		|||
        Point refpt = rotate_vector.second.rotated(- rotate_vector.first);
 | 
			
		||||
        // _align_to_grid will not work correctly with positive pattern_shift.
 | 
			
		||||
        coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing;
 | 
			
		||||
        refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
 | 
			
		||||
        refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
 | 
			
		||||
        bounding_box.merge(_align_to_grid(
 | 
			
		||||
            bounding_box.min, 
 | 
			
		||||
            Point(line_spacing, line_spacing), 
 | 
			
		||||
| 
						 | 
				
			
			@ -2747,12 +2762,93 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
 | 
			
		|||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define FILL_MULTIPLE_SWEEPS_NEW
 | 
			
		||||
 | 
			
		||||
#ifdef FILL_MULTIPLE_SWEEPS_NEW
 | 
			
		||||
bool FillRectilinear2::fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out)
 | 
			
		||||
{
 | 
			
		||||
    assert(sweep_params.size() > 1);
 | 
			
		||||
    assert(! params.full_infill());
 | 
			
		||||
    params.density /= double(sweep_params.size());
 | 
			
		||||
    assert(params.density > 0.0001f && params.density <= 1.f);
 | 
			
		||||
 | 
			
		||||
    ExPolygonWithOffset poly_with_offset_base(surface->expolygon, 0, float(scale_(this->overlap - 0.5 * this->spacing)));
 | 
			
		||||
    if (poly_with_offset_base.n_contours == 0)
 | 
			
		||||
        // Not a single infill line fits.
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
    Polylines fill_lines;
 | 
			
		||||
    coord_t line_spacing = coord_t(scale_(this->spacing) / params.density);
 | 
			
		||||
    std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
 | 
			
		||||
    for (const SweepParams &sweep : sweep_params) {
 | 
			
		||||
        size_t n_fill_lines_initial = fill_lines.size();
 | 
			
		||||
 | 
			
		||||
        // Rotate polygons so that we can work with vertical lines here
 | 
			
		||||
        double angle = rotate_vector.first + sweep.angle_base;
 | 
			
		||||
        ExPolygonWithOffset poly_with_offset(poly_with_offset_base, - angle);
 | 
			
		||||
        BoundingBox bounding_box = poly_with_offset.bounding_box_src();
 | 
			
		||||
        // extend bounding box so that our pattern will be aligned with other layers
 | 
			
		||||
        // Transform the reference point to the rotated coordinate system.
 | 
			
		||||
        Point refpt = rotate_vector.second.rotated(- angle);
 | 
			
		||||
        // _align_to_grid will not work correctly with positive pattern_shift.
 | 
			
		||||
        coord_t pattern_shift_scaled = coord_t(scale_(sweep.pattern_shift)) % line_spacing;
 | 
			
		||||
        refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
 | 
			
		||||
        bounding_box.merge(_align_to_grid(bounding_box.min, Point(line_spacing, line_spacing), refpt));
 | 
			
		||||
 | 
			
		||||
        // Intersect a set of euqally spaced vertical lines wiht expolygon.
 | 
			
		||||
        // n_vlines = ceil(bbox_width / line_spacing)
 | 
			
		||||
        const size_t n_vlines = (bounding_box.max.x() - bounding_box.min.x() + line_spacing - 1) / line_spacing;
 | 
			
		||||
        const double cos_a    = cos(angle);
 | 
			
		||||
        const double sin_a    = sin(angle);
 | 
			
		||||
        for (const SegmentedIntersectionLine &vline : slice_region_by_vertical_lines(poly_with_offset, n_vlines, bounding_box.min.x(), line_spacing)) {
 | 
			
		||||
            for (auto it = vline.intersections.begin(); it != vline.intersections.end();) {
 | 
			
		||||
                auto it_low  = it ++;
 | 
			
		||||
                assert(it_low->type == SegmentIntersection::OUTER_LOW);
 | 
			
		||||
                if (it_low->type != SegmentIntersection::OUTER_LOW)
 | 
			
		||||
                    continue;
 | 
			
		||||
                auto it_high = it;
 | 
			
		||||
                assert(it_high->type == SegmentIntersection::OUTER_HIGH);
 | 
			
		||||
                if (it_high->type == SegmentIntersection::OUTER_HIGH) {
 | 
			
		||||
                    fill_lines.emplace_back(Point(vline.pos, it_low->pos()).rotated(cos_a, sin_a), Point(vline.pos, it_high->pos()).rotated(cos_a, sin_a));
 | 
			
		||||
                    ++ it;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (fill_lines.size() > 1)
 | 
			
		||||
        fill_lines = chain_polylines(std::move(fill_lines));
 | 
			
		||||
 | 
			
		||||
    if (params.dont_connect || fill_lines.size() <= 1)
 | 
			
		||||
        append(polylines_out, std::move(fill_lines));
 | 
			
		||||
    else {
 | 
			
		||||
//        coord_t hook_length = 0;
 | 
			
		||||
        coord_t hook_length = coord_t(scale_(this->spacing)) * 5;
 | 
			
		||||
        connect_infill(std::move(fill_lines), poly_with_offset_base.polygons_outer, get_extents(surface->expolygon.contour), polylines_out, this->spacing, params, hook_length);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
bool FillRectilinear2::fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out)
 | 
			
		||||
{
 | 
			
		||||
    params.density /= double(sweep_params.size());
 | 
			
		||||
    bool success = true;
 | 
			
		||||
    int  idx = 0;
 | 
			
		||||
    for (const SweepParams &sweep_param : sweep_params) {
 | 
			
		||||
        if (++ idx == 3)
 | 
			
		||||
            params.dont_connect = true;
 | 
			
		||||
        success &= this->fill_surface_by_lines(surface, params, sweep_param.angle_base, sweep_param.pattern_shift, polylines_out);
 | 
			
		||||
    }
 | 
			
		||||
    return success;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
Polylines FillRectilinear2::fill_surface(const Surface *surface, const FillParams ¶ms)
 | 
			
		||||
{
 | 
			
		||||
    Polylines polylines_out;
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out)) {
 | 
			
		||||
        printf("FillRectilinear2::fill_surface() failed to fill a region.\n");
 | 
			
		||||
    }
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params, 0.f, 0.f, polylines_out))
 | 
			
		||||
        BOOST_LOG_TRIVIAL(error) << "FillRectilinear2::fill_surface() failed to fill a region.";
 | 
			
		||||
    return polylines_out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2761,72 +2857,53 @@ Polylines FillMonotonic::fill_surface(const Surface *surface, const FillParams &
 | 
			
		|||
    FillParams params2 = params;
 | 
			
		||||
    params2.monotonic = true;
 | 
			
		||||
    Polylines polylines_out;
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params2, 0.f, 0.f, polylines_out)) {
 | 
			
		||||
        printf("FillMonotonic::fill_surface() failed to fill a region.\n");
 | 
			
		||||
    }
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params2, 0.f, 0.f, polylines_out))
 | 
			
		||||
        BOOST_LOG_TRIVIAL(error) << "FillMonotonous::fill_surface() failed to fill a region.";
 | 
			
		||||
    return polylines_out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Polylines FillGrid2::fill_surface(const Surface *surface, const FillParams ¶ms)
 | 
			
		||||
{
 | 
			
		||||
    // Each linear fill covers half of the target coverage.
 | 
			
		||||
    FillParams params2 = params;
 | 
			
		||||
    params2.density *= 0.5f;
 | 
			
		||||
    Polylines polylines_out;
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params2, 0.f, 0.f, polylines_out) ||
 | 
			
		||||
        ! fill_surface_by_lines(surface, params2, float(M_PI / 2.), 0.f, polylines_out)) {
 | 
			
		||||
        printf("FillGrid2::fill_surface() failed to fill a region.\n");
 | 
			
		||||
    }
 | 
			
		||||
    if (! this->fill_surface_by_multilines(
 | 
			
		||||
            surface, params,
 | 
			
		||||
            { { 0.f, 0.f }, { float(M_PI / 2.), 0.f } },
 | 
			
		||||
            polylines_out))
 | 
			
		||||
        BOOST_LOG_TRIVIAL(error) << "FillGrid2::fill_surface() failed to fill a region.";
 | 
			
		||||
    return polylines_out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Polylines FillTriangles::fill_surface(const Surface *surface, const FillParams ¶ms)
 | 
			
		||||
{
 | 
			
		||||
    // Each linear fill covers 1/3 of the target coverage.
 | 
			
		||||
    FillParams params2 = params;
 | 
			
		||||
    params2.density *= 0.333333333f;
 | 
			
		||||
    FillParams params3 = params2;
 | 
			
		||||
    params3.dont_connect = true;
 | 
			
		||||
    Polylines polylines_out;
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) ||
 | 
			
		||||
        ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) ||
 | 
			
		||||
        ! fill_surface_by_lines(surface, params3, float(2. * M_PI / 3.), 0., polylines_out)) {
 | 
			
		||||
        printf("FillTriangles::fill_surface() failed to fill a region.\n");
 | 
			
		||||
    }
 | 
			
		||||
    if (! this->fill_surface_by_multilines(
 | 
			
		||||
            surface, params,
 | 
			
		||||
            { { 0.f, 0.f }, { float(M_PI / 3.), 0.f }, { float(2. * M_PI / 3.), 0. } },
 | 
			
		||||
            polylines_out))
 | 
			
		||||
        BOOST_LOG_TRIVIAL(error) << "FillTriangles::fill_surface() failed to fill a region.";
 | 
			
		||||
    return polylines_out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Polylines FillStars::fill_surface(const Surface *surface, const FillParams ¶ms)
 | 
			
		||||
{
 | 
			
		||||
    // Each linear fill covers 1/3 of the target coverage.
 | 
			
		||||
    FillParams params2 = params;
 | 
			
		||||
    params2.density *= 0.333333333f;
 | 
			
		||||
    FillParams params3 = params2;
 | 
			
		||||
    params3.dont_connect = true;
 | 
			
		||||
    Polylines polylines_out;
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params2, 0.f, 0., polylines_out) ||
 | 
			
		||||
        ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), 0., polylines_out) ||
 | 
			
		||||
        ! fill_surface_by_lines(surface, params3, float(2. * M_PI / 3.), 0.5 * this->spacing / params2.density, polylines_out)) {
 | 
			
		||||
        printf("FillStars::fill_surface() failed to fill a region.\n");
 | 
			
		||||
    }
 | 
			
		||||
    if (! this->fill_surface_by_multilines(
 | 
			
		||||
            surface, params,
 | 
			
		||||
            { { 0.f, 0.f }, { float(M_PI / 3.), 0.f }, { float(2. * M_PI / 3.), float((3./2.) * this->spacing / params.density) } },
 | 
			
		||||
            polylines_out))
 | 
			
		||||
        BOOST_LOG_TRIVIAL(error) << "FillStars::fill_surface() failed to fill a region.";
 | 
			
		||||
    return polylines_out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶ms)
 | 
			
		||||
{
 | 
			
		||||
    // Each linear fill covers 1/3 of the target coverage.
 | 
			
		||||
    FillParams params2 = params;
 | 
			
		||||
    params2.density *= 0.333333333f;
 | 
			
		||||
    FillParams params3 = params2;
 | 
			
		||||
    params3.dont_connect = true;
 | 
			
		||||
    Polylines polylines_out;
 | 
			
		||||
    coordf_t dx = sqrt(0.5) * z;
 | 
			
		||||
    if (! fill_surface_by_lines(surface, params2, 0.f, float(dx), polylines_out) ||
 | 
			
		||||
        ! fill_surface_by_lines(surface, params2, float(M_PI / 3.), - float(dx), polylines_out) ||
 | 
			
		||||
        // Rotated by PI*2/3 + PI to achieve reverse sloping wall.
 | 
			
		||||
        ! fill_surface_by_lines(surface, params3, float(M_PI * 2. / 3.), float(dx), polylines_out)) {
 | 
			
		||||
        printf("FillCubic::fill_surface() failed to fill a region.\n");
 | 
			
		||||
    } 
 | 
			
		||||
    if (! this->fill_surface_by_multilines(
 | 
			
		||||
            surface, params, 
 | 
			
		||||
            { { 0.f, float(dx) }, { float(M_PI / 3.), - float(dx) }, { float(M_PI * 2. / 3.), float(dx) } },
 | 
			
		||||
            polylines_out))
 | 
			
		||||
        BOOST_LOG_TRIVIAL(error) << "FillCubic::fill_surface() failed to fill a region.";
 | 
			
		||||
    return polylines_out; 
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,7 +17,16 @@ public:
 | 
			
		|||
    virtual Polylines fill_surface(const Surface *surface, const FillParams ¶ms);
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    // Fill by single directional lines, interconnect the lines along perimeters.
 | 
			
		||||
	bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Fill by multiple sweeps of differing directions.
 | 
			
		||||
    struct SweepParams {
 | 
			
		||||
        float angle_base;
 | 
			
		||||
        float pattern_shift;
 | 
			
		||||
    };
 | 
			
		||||
    bool fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class FillMonotonic : public FillRectilinear2
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue