mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-11-02 20:51:23 -07:00 
			
		
		
		
	Fix of [2.3.0-alpha4] Crash - several models cause crash when slicing #5208
Fixed some issues in internal anchors of the Adaptive Cubic infill. The ugly and dangerous implicit casting operators in Line, MultiPoint, Polyline and Polygon were made explicit.
This commit is contained in:
		
							parent
							
								
									10c41290fd
								
							
						
					
					
						commit
						62bdc192d8
					
				
					 25 changed files with 237 additions and 110 deletions
				
			
		| 
						 | 
				
			
			@ -37,7 +37,8 @@ struct SurfaceFillParams
 | 
			
		|||
    bool        	dont_adjust = false;
 | 
			
		||||
    // Length of the infill anchor along the perimeter line.
 | 
			
		||||
    // 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
 | 
			
		||||
    float 			anchor_length = 1000.f;
 | 
			
		||||
    float 			anchor_length     = 1000.f;
 | 
			
		||||
    float 			anchor_length_max = 1000.f;
 | 
			
		||||
 | 
			
		||||
    // width, height of extrusion, nozzle diameter, is bridge
 | 
			
		||||
    // For the output, for fill generator.
 | 
			
		||||
| 
						 | 
				
			
			@ -68,6 +69,7 @@ struct SurfaceFillParams
 | 
			
		|||
		RETURN_COMPARE_NON_EQUAL(density);
 | 
			
		||||
		RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
 | 
			
		||||
		RETURN_COMPARE_NON_EQUAL(anchor_length);
 | 
			
		||||
		RETURN_COMPARE_NON_EQUAL(anchor_length_max);
 | 
			
		||||
		RETURN_COMPARE_NON_EQUAL(flow.width);
 | 
			
		||||
		RETURN_COMPARE_NON_EQUAL(flow.height);
 | 
			
		||||
		RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +87,8 @@ struct SurfaceFillParams
 | 
			
		|||
				this->angle   			== rhs.angle   			&&
 | 
			
		||||
				this->density   		== rhs.density   		&&
 | 
			
		||||
				this->dont_adjust   	== rhs.dont_adjust 		&&
 | 
			
		||||
				this->anchor_length		== rhs.anchor_length    &&
 | 
			
		||||
				this->anchor_length  	== rhs.anchor_length    &&
 | 
			
		||||
				this->anchor_length_max == rhs.anchor_length_max &&
 | 
			
		||||
				this->flow 				== rhs.flow 			&&
 | 
			
		||||
				this->extrusion_role	== rhs.extrusion_role;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -171,8 +174,12 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
 | 
			
		|||
		            // Anchor a sparse infill to inner perimeters with the following anchor length:
 | 
			
		||||
			        params.anchor_length = float(region_config.infill_anchor);
 | 
			
		||||
			        if (region_config.infill_anchor.percent)
 | 
			
		||||
			        	params.anchor_length *= 0.01 * params.spacing;
 | 
			
		||||
			        	params.anchor_length = float(params.anchor_length * 0.01 * params.spacing);
 | 
			
		||||
			        params.anchor_length_max = float(region_config.infill_anchor_max);
 | 
			
		||||
			        if (region_config.infill_anchor_max.percent)
 | 
			
		||||
			        	params.anchor_length_max = float(params.anchor_length_max * 0.01 * params.spacing);
 | 
			
		||||
		        }
 | 
			
		||||
		        params.anchor_length = std::min(params.anchor_length, params.anchor_length_max);
 | 
			
		||||
 | 
			
		||||
		        auto it_params = set_surface_params.find(params);
 | 
			
		||||
		        if (it_params == set_surface_params.end())
 | 
			
		||||
| 
						 | 
				
			
			@ -376,9 +383,10 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
 | 
			
		|||
 | 
			
		||||
        // apply half spacing using this flow's own spacing and generate infill
 | 
			
		||||
        FillParams params;
 | 
			
		||||
        params.density 		 = float(0.01 * surface_fill.params.density);
 | 
			
		||||
        params.dont_adjust 	 = surface_fill.params.dont_adjust; // false
 | 
			
		||||
        params.anchor_length = surface_fill.params.anchor_length;
 | 
			
		||||
        params.density 		     = float(0.01 * surface_fill.params.density);
 | 
			
		||||
        params.dont_adjust 	     = surface_fill.params.dont_adjust; // false
 | 
			
		||||
        params.anchor_length     = surface_fill.params.anchor_length;
 | 
			
		||||
		params.anchor_length_max = surface_fill.params.anchor_length_max;
 | 
			
		||||
 | 
			
		||||
        for (ExPolygon &expoly : surface_fill.expolygons) {
 | 
			
		||||
			// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -667,9 +667,26 @@ static inline rtree_segment_t mk_rtree_seg(const Line &l) {
 | 
			
		|||
// 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 double scaled_offset, 
 | 
			
		||||
    const int hook_length, double scaled_trim_distance, 
 | 
			
		||||
    const coordf_t hook_length, double scaled_trim_distance, 
 | 
			
		||||
    const rtree_t &rtree, const Lines &lines_src)
 | 
			
		||||
{
 | 
			
		||||
    if (hook_length < SCALED_EPSILON)
 | 
			
		||||
        // Ignore open hooks.
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
    {
 | 
			
		||||
        const Vec2d  v  = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
 | 
			
		||||
        const Vec2d  va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
 | 
			
		||||
        const double l2 = v.squaredNorm();  // avoid a sqrt
 | 
			
		||||
        assert(l2 > 0.);
 | 
			
		||||
        const double t  = va.dot(v) / l2;
 | 
			
		||||
        assert(t > 0. && t < 1.);
 | 
			
		||||
        const double          d  = (t * v - va).norm();
 | 
			
		||||
        assert(d < 1000.);
 | 
			
		||||
    }
 | 
			
		||||
#endif // NDEBUG
 | 
			
		||||
 | 
			
		||||
    // Trim the hook start by the infill line it will connect to.
 | 
			
		||||
    Point hook_start;
 | 
			
		||||
    bool  intersection_found = intersection.intersect_line->intersection(
 | 
			
		||||
| 
						 | 
				
			
			@ -700,7 +717,7 @@ static void add_hook(
 | 
			
		|||
        const std::vector<std::pair<rtree_segment_t, size_t>> &hook_intersections,
 | 
			
		||||
        bool self_intersection, const std::optional<Line> &self_intersection_line, const Point &self_intersection_point) {
 | 
			
		||||
        // No hook is longer than hook_length, there shouldn't be any intersection closer than that.
 | 
			
		||||
        auto max_length = double(hook_length);
 | 
			
		||||
        auto max_length = hook_length;
 | 
			
		||||
        auto update_max_length = [&max_length](double d) {
 | 
			
		||||
            if (d < max_length)
 | 
			
		||||
                max_length = d;
 | 
			
		||||
| 
						 | 
				
			
			@ -757,15 +774,32 @@ static void add_hook(
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &boundary, const double spacing, const int hook_length)
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
bool validate_intersection_t_joint(const Intersection &intersection)
 | 
			
		||||
{
 | 
			
		||||
    const Vec2d  v = (intersection.closest_line->b - intersection.closest_line->a).cast<double>();
 | 
			
		||||
    const Vec2d  va = (intersection.intersect_point - intersection.closest_line->a).cast<double>();
 | 
			
		||||
    const double l2 = v.squaredNorm();  // avoid a sqrt
 | 
			
		||||
    assert(l2 > 0.);
 | 
			
		||||
    const double t = va.dot(v);
 | 
			
		||||
    assert(t > SCALED_EPSILON && t < l2 - SCALED_EPSILON);
 | 
			
		||||
    const double d = ((t / l2) * v - va).norm();
 | 
			
		||||
    assert(d < 1000.);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
bool validate_intersections(const std::vector<Intersection> &intersections)
 | 
			
		||||
{
 | 
			
		||||
    for (const Intersection& intersection : intersections)
 | 
			
		||||
        assert(validate_intersection_t_joint(intersection));
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
#endif // NDEBUG
 | 
			
		||||
 | 
			
		||||
static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &boundary, const double spacing, const coordf_t hook_length, const coordf_t hook_length_max)
 | 
			
		||||
{
 | 
			
		||||
    rtree_t rtree;
 | 
			
		||||
    size_t  poly_idx = 0;
 | 
			
		||||
 | 
			
		||||
    Lines lines_src;
 | 
			
		||||
    lines_src.reserve(lines.size());
 | 
			
		||||
    std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Line& l) { return Polyline{ l.a, l.b }; });
 | 
			
		||||
 | 
			
		||||
    // 19% overlap, slightly lower than the allowed overlap in Fill::connect_infill()
 | 
			
		||||
    const float scaled_offset           = float(scale_(spacing) * 0.81);
 | 
			
		||||
    // 25% overlap
 | 
			
		||||
| 
						 | 
				
			
			@ -814,16 +848,19 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
                    }
 | 
			
		||||
                    return std::make_pair(static_cast<Polyline*>(nullptr), false);
 | 
			
		||||
                };
 | 
			
		||||
                auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(), &poly);
 | 
			
		||||
                auto collinear_front = collinear_segment(poly.points.front(), poly.points.back(),  &poly);
 | 
			
		||||
                auto collinear_back  = collinear_segment(poly.points.back(),  poly.points.front(), &poly);
 | 
			
		||||
                assert(! collinear_front.first || ! collinear_back.first || collinear_front.first != collinear_back.first);
 | 
			
		||||
                if (collinear_front.first) {
 | 
			
		||||
                    Polyline &other = *collinear_front.first;
 | 
			
		||||
                    assert(&other != &poly);
 | 
			
		||||
                    poly.points.front() = collinear_front.second ? other.points.back() : other.points.front();
 | 
			
		||||
                    other.points.clear();
 | 
			
		||||
                }
 | 
			
		||||
                auto collinear_back = collinear_segment(poly.points.back(), poly.points.front(), &poly);
 | 
			
		||||
                if (collinear_back.first) {
 | 
			
		||||
                    Polyline &other = *collinear_front.first;
 | 
			
		||||
                    poly.points.back() = collinear_front.second ? other.points.back() : other.points.front();
 | 
			
		||||
                    Polyline &other = *collinear_back.first;
 | 
			
		||||
                    assert(&other != &poly);
 | 
			
		||||
                    poly.points.back() = collinear_back.second ? other.points.back() : other.points.front();
 | 
			
		||||
                    other.points.clear();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -831,6 +868,12 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Convert input polylines to lines_src after the colinear segments were merged.
 | 
			
		||||
    Lines lines_src;
 | 
			
		||||
    lines_src.reserve(lines.size());
 | 
			
		||||
    std::transform(lines.begin(), lines.end(), std::back_inserter(lines_src), [](const Polyline &pl) { 
 | 
			
		||||
        return pl.empty() ? Line(Point(0, 0), Point(0, 0)) : Line(pl.points.front(), pl.points.back()); });
 | 
			
		||||
 | 
			
		||||
    sort_remove_duplicates(lines_touching_at_endpoints);
 | 
			
		||||
 | 
			
		||||
    std::vector<Intersection> intersections;
 | 
			
		||||
| 
						 | 
				
			
			@ -854,23 +897,38 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
            // Find the nearest line from the start point of the line.
 | 
			
		||||
            std::optional<size_t> tjoint_front, tjoint_back;
 | 
			
		||||
            {
 | 
			
		||||
                auto has_tjoint = [&closest, line_idx, &rtree, &lines](const Point &pt) {
 | 
			
		||||
                    auto filter_itself = [line_idx](const auto &item) { return item.second != line_idx; };
 | 
			
		||||
                auto has_tjoint = [&closest, line_idx, &rtree, &lines, &lines_src](const Point &pt) {
 | 
			
		||||
                    auto filter_t_joint = [line_idx, &lines_src, pt](const auto &item) { 
 | 
			
		||||
                        if (item.second != line_idx) {
 | 
			
		||||
                            // Verify that the point projects onto the line.
 | 
			
		||||
                            const Line  &line = lines_src[item.second];
 | 
			
		||||
                            const Vec2d  v  = (line.b - line.a).cast<double>();
 | 
			
		||||
                            const Vec2d  va = (pt - line.a).cast<double>();
 | 
			
		||||
                            const double l2 = v.squaredNorm();  // avoid a sqrt
 | 
			
		||||
                            if (l2 > 0.) {
 | 
			
		||||
                                const double t = va.dot(v);
 | 
			
		||||
                                return t > SCALED_EPSILON && t < l2 - SCALED_EPSILON;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        return false;
 | 
			
		||||
                    };
 | 
			
		||||
                    closest.clear();
 | 
			
		||||
                    rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_itself), std::back_inserter(closest));
 | 
			
		||||
                    const Polyline &pl = lines[closest.front().second];
 | 
			
		||||
                    rtree.query(bgi::nearest(mk_rtree_point(pt), 1) && bgi::satisfies(filter_t_joint), std::back_inserter(closest));
 | 
			
		||||
                    std::optional<size_t> out;
 | 
			
		||||
                    if (pl.points.empty()) {
 | 
			
		||||
                        // The closest infill line was already dropped as it was too short.
 | 
			
		||||
                        // Such an infill line should not make a T-joint anyways.
 | 
			
		||||
#if 0 // #ifndef NDEBUG
 | 
			
		||||
                        const auto &seg = closest.front().first;
 | 
			
		||||
                        struct Linef { Vec2d a; Vec2d b; };
 | 
			
		||||
                        Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
 | 
			
		||||
                        assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
 | 
			
		||||
#endif // NDEBUG
 | 
			
		||||
                    } else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
 | 
			
		||||
                        out = closest.front().second;
 | 
			
		||||
                    if (! closest.empty()) {
 | 
			
		||||
                        const Polyline &pl = lines[closest.front().second];
 | 
			
		||||
                        if (pl.points.empty()) {
 | 
			
		||||
                            // The closest infill line was already dropped as it was too short.
 | 
			
		||||
                            // Such an infill line should not make a T-joint anyways.
 | 
			
		||||
    #if 0 // #ifndef NDEBUG
 | 
			
		||||
                            const auto &seg = closest.front().first;
 | 
			
		||||
                            struct Linef { Vec2d a; Vec2d b; };
 | 
			
		||||
                            Linef l { { bg::get<0, 0>(seg), bg::get<0, 1>(seg) }, { bg::get<1, 0>(seg), bg::get<1, 1>(seg) } };
 | 
			
		||||
                            assert(line_alg::distance_to_squared(l, Vec2d(pt.cast<double>())) > 1000 * 1000);
 | 
			
		||||
    #endif // NDEBUG
 | 
			
		||||
                        } else if (((Line)pl).distance_to_squared(pt) <= 1000 * 1000)
 | 
			
		||||
                            out = closest.front().second;
 | 
			
		||||
                    }
 | 
			
		||||
                    return out;
 | 
			
		||||
                };
 | 
			
		||||
                // Refuse to create a T-joint if the infill lines touch at their ends.
 | 
			
		||||
| 
						 | 
				
			
			@ -912,12 +970,16 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
                    // A shorter line than spacing could produce a degenerate polyline.
 | 
			
		||||
                    line.points.clear();
 | 
			
		||||
                } else if (anchor) {
 | 
			
		||||
                    if (tjoint_front)
 | 
			
		||||
                    if (tjoint_front) {
 | 
			
		||||
                        // T-joint of line's front point with the 'closest' line.
 | 
			
		||||
                        intersections.emplace_back(lines_src[*tjoint_front], lines_src[line_idx], &line, front_point, true);
 | 
			
		||||
                    if (tjoint_back)
 | 
			
		||||
                        assert(validate_intersection_t_joint(intersections.back()));
 | 
			
		||||
                    }
 | 
			
		||||
                    if (tjoint_back) {
 | 
			
		||||
                        // T-joint of line's back point with the 'closest' line.
 | 
			
		||||
                        intersections.emplace_back(lines_src[*tjoint_back],  lines_src[line_idx], &line, back_point,  false);
 | 
			
		||||
                        assert(validate_intersection_t_joint(intersections.back()));
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (tjoint_front)
 | 
			
		||||
                        // T joint at the front at a 60 degree angle, the line is very short.
 | 
			
		||||
| 
						 | 
				
			
			@ -940,6 +1002,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
                ++ it;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    assert(validate_intersections(intersections));
 | 
			
		||||
 | 
			
		||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
 | 
			
		||||
    static int iRun = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1106,7 +1169,7 @@ static Polylines connect_lines_using_hooks(Polylines &&lines, const ExPolygon &b
 | 
			
		|||
            }
 | 
			
		||||
            Points &first_points  = first_i.intersect_pl->points;
 | 
			
		||||
            Points &second_points = nearest_i.intersect_pl->points;
 | 
			
		||||
            could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(3. * hook_length);
 | 
			
		||||
            could_connect &= (nearest_i_point - first_i_point).cast<double>().squaredNorm() <= Slic3r::sqr(hook_length_max);
 | 
			
		||||
            if (could_connect) {
 | 
			
		||||
                // Both intersections are so close that their polylines can be connected.
 | 
			
		||||
                // Verify that no other infill line intersects this anchor line.
 | 
			
		||||
| 
						 | 
				
			
			@ -1219,7 +1282,7 @@ bool has_no_collinear_lines(const Polylines &polylines)
 | 
			
		|||
        const Point* operator()(const LineEnd &pt) const { return &pt.point(); }
 | 
			
		||||
    };
 | 
			
		||||
    typedef ClosestPointInRadiusLookup<LineEnd, LineEndAccessor> ClosestPointLookupType;
 | 
			
		||||
    ClosestPointLookupType closest_end_point_lookup(1001. * sqrt(2.));
 | 
			
		||||
    ClosestPointLookupType closest_end_point_lookup(coord_t(1001. * sqrt(2.)));
 | 
			
		||||
    for (const Polyline& pl : polylines) {
 | 
			
		||||
//        assert(pl.points.size() == 2);
 | 
			
		||||
        auto line_start = LineEnd(&pl, true);
 | 
			
		||||
| 
						 | 
				
			
			@ -1321,9 +1384,10 @@ void Filler::_fill_surface_single(
 | 
			
		|||
    }
 | 
			
		||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
 | 
			
		||||
 | 
			
		||||
    const auto hook_length = coord_t(std::min(scale_(this->spacing * 5), scale_(params.anchor_length)));
 | 
			
		||||
    const auto hook_length     = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
 | 
			
		||||
    const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
 | 
			
		||||
 | 
			
		||||
    Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length) : std::move(all_polylines);
 | 
			
		||||
    Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines);
 | 
			
		||||
 | 
			
		||||
#ifdef ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT
 | 
			
		||||
    {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -200,10 +200,10 @@ struct ContourIntersectionPoint {
 | 
			
		|||
 | 
			
		||||
    // Could extrude a complete segment from this to this->prev_on_contour.
 | 
			
		||||
    bool                        could_connect_prev() const throw() 
 | 
			
		||||
        { return ! this->consumed && this->prev_on_contour && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
 | 
			
		||||
        { return ! this->consumed && this->prev_on_contour != this && ! this->prev_on_contour->consumed && ! this->prev_trimmed && ! this->prev_on_contour->next_trimmed; }
 | 
			
		||||
    // Could extrude a complete segment from this to this->next_on_contour.
 | 
			
		||||
    bool                        could_connect_next() const throw() 
 | 
			
		||||
        { return ! this->consumed && this->next_on_contour && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
 | 
			
		||||
        { return ! this->consumed && this->next_on_contour != this && ! this->next_on_contour->consumed && ! this->next_trimmed && ! this->next_on_contour->prev_trimmed; }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Distance from param1 to param2 when going counter-clockwise.
 | 
			
		||||
| 
						 | 
				
			
			@ -390,7 +390,12 @@ static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, size
 | 
			
		|||
 | 
			
		||||
static void take(Polyline &pl1, const Polyline &pl2, const Points &contour, ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise)
 | 
			
		||||
{
 | 
			
		||||
    assert(cp_start->prev_on_contour != nullptr);
 | 
			
		||||
    assert(cp_start->next_on_contour != nullptr);
 | 
			
		||||
    assert(cp_end  ->prev_on_contour != nullptr);
 | 
			
		||||
    assert(cp_end  ->next_on_contour != nullptr);
 | 
			
		||||
    assert(cp_start != cp_end);
 | 
			
		||||
 | 
			
		||||
    take(pl1, pl2, contour, cp_start->point_idx, cp_end->point_idx, clockwise);
 | 
			
		||||
 | 
			
		||||
    // Mark the contour segments in between cp_start and cp_end as consumed.
 | 
			
		||||
| 
						 | 
				
			
			@ -410,7 +415,12 @@ static void take_limited(
 | 
			
		|||
    ContourIntersectionPoint *cp_start, ContourIntersectionPoint *cp_end, bool clockwise, float take_max_length, float line_half_width)
 | 
			
		||||
{
 | 
			
		||||
#ifndef NDEBUG
 | 
			
		||||
    assert(cp_start != cp_end);
 | 
			
		||||
    // This is a valid case, where a single infill line connect to two different contours (outer contour + hole or two holes).
 | 
			
		||||
//    assert(cp_start != cp_end);
 | 
			
		||||
    assert(cp_start->prev_on_contour != nullptr);
 | 
			
		||||
    assert(cp_start->next_on_contour != nullptr);
 | 
			
		||||
    assert(cp_end  ->prev_on_contour != nullptr);
 | 
			
		||||
    assert(cp_end  ->next_on_contour != nullptr);
 | 
			
		||||
    assert(pl1.size() >= 2);
 | 
			
		||||
    assert(contour.size() + 1 == params.size());
 | 
			
		||||
#endif /* NDEBUG */
 | 
			
		||||
| 
						 | 
				
			
			@ -438,8 +448,18 @@ static void take_limited(
 | 
			
		|||
    float length = params.back();
 | 
			
		||||
    float length_to_go = take_max_length;
 | 
			
		||||
    cp_start->consumed = true;
 | 
			
		||||
    if (clockwise) {
 | 
			
		||||
    if (cp_start == cp_end) {
 | 
			
		||||
        length_to_go = std::max(0.f, std::min(length_to_go, length - line_half_width));
 | 
			
		||||
        length_to_go = std::min(length_to_go, clockwise ? cp_start->contour_not_taken_length_prev : cp_start->contour_not_taken_length_next);
 | 
			
		||||
        cp_start->consume_prev();
 | 
			
		||||
        cp_start->consume_next();
 | 
			
		||||
        if (length_to_go > SCALED_EPSILON)
 | 
			
		||||
            clockwise ?
 | 
			
		||||
                take_cw_limited (pl1, contour, params, cp_start->point_idx, cp_start->point_idx, length_to_go) :
 | 
			
		||||
                take_ccw_limited(pl1, contour, params, cp_start->point_idx, cp_start->point_idx, length_to_go);
 | 
			
		||||
    } else if (clockwise) {
 | 
			
		||||
        // Going clockwise from cp_start to cp_end.
 | 
			
		||||
        assert(cp_start != cp_end);
 | 
			
		||||
        for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->prev_on_contour) {
 | 
			
		||||
            // Length of the segment from cp to cp->prev_on_contour.
 | 
			
		||||
            float l = closed_contour_distance_cw(cp->param, cp->prev_on_contour->param, length);
 | 
			
		||||
| 
						 | 
				
			
			@ -461,6 +481,7 @@ static void take_limited(
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        assert(cp_start != cp_end);
 | 
			
		||||
        for (ContourIntersectionPoint *cp = cp_start; cp != cp_end; cp = cp->next_on_contour) {
 | 
			
		||||
            float l = closed_contour_distance_ccw(cp->param, cp->next_on_contour->param, length);
 | 
			
		||||
            length_to_go = std::min(length_to_go, cp->contour_not_taken_length_next);
 | 
			
		||||
| 
						 | 
				
			
			@ -869,6 +890,10 @@ void mark_boundary_segments_touching_infill(
 | 
			
		|||
			for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++ it_contour_and_segment) {
 | 
			
		||||
				// End points of the line segment and their vector.
 | 
			
		||||
				auto segment = this->grid.segment(*it_contour_and_segment);
 | 
			
		||||
                std::vector<ContourIntersectionPoint*> &intersections = boundary_intersections[it_contour_and_segment->first];
 | 
			
		||||
                if (intersections.empty())
 | 
			
		||||
                    // There is no infil line touching this contour, thus effort will be saved to calculate overlap with other infill lines.
 | 
			
		||||
                    continue;
 | 
			
		||||
				const Vec2d seg_pt1 = segment.first.cast<double>();
 | 
			
		||||
				const Vec2d seg_pt2 = segment.second.cast<double>();
 | 
			
		||||
                std::pair<double, double> interval;
 | 
			
		||||
| 
						 | 
				
			
			@ -892,20 +917,23 @@ void mark_boundary_segments_touching_infill(
 | 
			
		|||
                    const float param_overlap1 = param_seg_pt1 + interval.first;
 | 
			
		||||
                    const float param_overlap2 = param_seg_pt1 + interval.second;
 | 
			
		||||
                    // 2) Find the ContourIntersectionPoints before param_overlap1 and after param_overlap2.
 | 
			
		||||
                    std::vector<ContourIntersectionPoint*> &intersections = boundary_intersections[it_contour_and_segment->first];
 | 
			
		||||
                    // Find the span of ContourIntersectionPoints, that is trimmed by the interval (param_overlap1, param_overlap2).
 | 
			
		||||
                    ContourIntersectionPoint *ip_low, *ip_high;
 | 
			
		||||
                    {
 | 
			
		||||
                    if (intersections.size() == 1) {
 | 
			
		||||
                        // Only a single infill line touches this contour.
 | 
			
		||||
                        ip_low = ip_high = intersections.front();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        assert(intersections.size() > 1);
 | 
			
		||||
                        auto it_low  = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap1](const ContourIntersectionPoint *l) { return l->param < param_overlap1; });
 | 
			
		||||
                        auto it_high = Slic3r::lower_bound_by_predicate(intersections.begin(), intersections.end(), [param_overlap2](const ContourIntersectionPoint *l) { return l->param < param_overlap2; });
 | 
			
		||||
                        ip_low  = it_low  == intersections.end() ? intersections.front() : *it_low;
 | 
			
		||||
                        ip_high = it_high == intersections.end() ? intersections.front() : *it_high;
 | 
			
		||||
                        if (ip_low->param != param_overlap1)
 | 
			
		||||
                            ip_low = ip_low->prev_on_contour;
 | 
			
		||||
                        assert(ip_low != ip_high);
 | 
			
		||||
                        // Verify that the interval (param_overlap1, param_overlap2) is inside the interval (ip_low->param, ip_high->param).
 | 
			
		||||
                        assert(cyclic_interval_inside_interval(ip_low->param, ip_high->param, param_overlap1, param_overlap2, contour_length));
 | 
			
		||||
                    }
 | 
			
		||||
                    assert(ip_low != ip_high);
 | 
			
		||||
                    // Verify that the interval (param_overlap1, param_overlap2) is inside the interval (ip_low->param, ip_high->param).
 | 
			
		||||
                    assert(cyclic_interval_inside_interval(ip_low->param, ip_high->param, param_overlap1, param_overlap2, contour_length));
 | 
			
		||||
                    assert(validate_boundary_intersections(boundary_intersections));
 | 
			
		||||
                    // Mark all ContourIntersectionPoints between ip_low and ip_high as consumed.
 | 
			
		||||
                    if (ip_low->next_on_contour != ip_high)
 | 
			
		||||
| 
						 | 
				
			
			@ -1068,8 +1096,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const Polygons &boundary_s
 | 
			
		|||
void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms)
 | 
			
		||||
{
 | 
			
		||||
	assert(! infill_ordered.empty());
 | 
			
		||||
    assert(params.anchor_length >= 0.01f);
 | 
			
		||||
    const auto anchor_length = float(scale_(params.anchor_length));
 | 
			
		||||
    assert(params.anchor_length     >= 0.f);
 | 
			
		||||
    assert(params.anchor_length_max >= 0.01f);
 | 
			
		||||
    assert(params.anchor_length_max >= params.anchor_length);
 | 
			
		||||
    const auto anchor_length     = float(scale_(params.anchor_length));
 | 
			
		||||
    const auto anchor_length_max = float(scale_(params.anchor_length_max));
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    append(polylines_out, infill_ordered);
 | 
			
		||||
| 
						 | 
				
			
			@ -1097,7 +1128,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
					EdgeGrid::Grid::ClosestPointResult cp = grid.closest_point(*pt, SCALED_EPSILON);
 | 
			
		||||
					if (cp.valid()) {
 | 
			
		||||
						// The infill end point shall lie on the contour.
 | 
			
		||||
						assert(cp.distance < 2.);
 | 
			
		||||
						assert(cp.distance <= 3.);
 | 
			
		||||
						intersection_points.emplace_back(cp, (&pl - infill_ordered.data()) * 2 + (pt == &pl.points.front() ? 0 : 1));
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			@ -1154,7 +1185,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
                    //add new point here
 | 
			
		||||
					contour_dst.emplace_back(pt);
 | 
			
		||||
				}
 | 
			
		||||
                if (pprev != pfirst) {
 | 
			
		||||
                if (pfirst) {
 | 
			
		||||
                    pprev->next_on_contour = pfirst;
 | 
			
		||||
                    pfirst->prev_on_contour = pprev;
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -1170,10 +1201,15 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
                ip->param = contour_params[ip->point_idx];
 | 
			
		||||
            // and measure distance to the previous and next intersection point.
 | 
			
		||||
            const float contour_length = contour_params.back();
 | 
			
		||||
            for (ContourIntersectionPoint *ip : contour_intersection_points) {
 | 
			
		||||
                ip->contour_not_taken_length_prev = closed_contour_distance_ccw(ip->prev_on_contour->param, ip->param, contour_length);
 | 
			
		||||
                ip->contour_not_taken_length_next = closed_contour_distance_ccw(ip->param, ip->next_on_contour->param, contour_length);
 | 
			
		||||
            }
 | 
			
		||||
            for (ContourIntersectionPoint *ip : contour_intersection_points) 
 | 
			
		||||
                if (ip->next_on_contour == ip) {
 | 
			
		||||
                    assert(ip->prev_on_contour == ip);
 | 
			
		||||
                    ip->contour_not_taken_length_prev = ip->contour_not_taken_length_next = contour_length;
 | 
			
		||||
                } else {
 | 
			
		||||
                    assert(ip->prev_on_contour != ip);
 | 
			
		||||
                    ip->contour_not_taken_length_prev = closed_contour_distance_ccw(ip->prev_on_contour->param, ip->param, contour_length);
 | 
			
		||||
                    ip->contour_not_taken_length_next = closed_contour_distance_ccw(ip->param, ip->next_on_contour->param, contour_length);
 | 
			
		||||
                }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		assert(boundary.size() == boundary_src.size());
 | 
			
		||||
| 
						 | 
				
			
			@ -1277,7 +1313,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
        idx_first = get_and_update_merged_with(idx_first);
 | 
			
		||||
        assert(idx_first < idx_second);
 | 
			
		||||
        assert(idx_second == merged_with[idx_second]);
 | 
			
		||||
        if (could_connect && length < anchor_length * 2.5) {
 | 
			
		||||
        if (could_connect && length < anchor_length_max) {
 | 
			
		||||
            // Take the complete contour.
 | 
			
		||||
            // Connect the two polygons using the boundary contour.
 | 
			
		||||
            take(infill_ordered[idx_first], infill_ordered[idx_second], boundary[cp1->contour_idx], cp1, cp2, connection_cost.reversed);
 | 
			
		||||
| 
						 | 
				
			
			@ -1299,10 +1335,11 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
    std::vector<Arc> arches;
 | 
			
		||||
    arches.reserve(map_infill_end_point_to_boundary.size());
 | 
			
		||||
    for (ContourIntersectionPoint &cp : map_infill_end_point_to_boundary)
 | 
			
		||||
        if (! cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
 | 
			
		||||
        if (cp.contour_idx != boundary_idx_unconnected && cp.next_on_contour != &cp && cp.could_connect_next())
 | 
			
		||||
            arches.push_back({ &cp, path_length_along_contour_ccw(&cp, cp.next_on_contour, boundary_params[cp.contour_idx].back()) });
 | 
			
		||||
    std::sort(arches.begin(), arches.end(), [](const auto &l, const auto &r) { return l.arc_length < r.arc_length; });
 | 
			
		||||
 | 
			
		||||
    //FIXME improve the Traveling Salesman problem with 2-opt and 3-opt local optimization.
 | 
			
		||||
    for (Arc &arc : arches)
 | 
			
		||||
        if (! arc.intersection->consumed && ! arc.intersection->next_on_contour->consumed) {
 | 
			
		||||
            // Indices of the polylines to be connected by a perimeter segment.
 | 
			
		||||
| 
						 | 
				
			
			@ -1315,7 +1352,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
            if (polyline_idx1 != polyline_idx2) {
 | 
			
		||||
                Polyline &polyline1 = infill_ordered[polyline_idx1];
 | 
			
		||||
                Polyline &polyline2 = infill_ordered[polyline_idx2];
 | 
			
		||||
                if (arc.arc_length < anchor_length * 2.5) {
 | 
			
		||||
                if (arc.arc_length < anchor_length_max) {
 | 
			
		||||
                    // Not closing a loop, connecting the lines.
 | 
			
		||||
                    assert(contour[cp1->point_idx] == polyline1.points.front() || contour[cp1->point_idx] == polyline1.points.back());
 | 
			
		||||
                    if (contour[cp1->point_idx] == polyline1.points.front())
 | 
			
		||||
| 
						 | 
				
			
			@ -1333,7 +1370,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
                        polyline2.points.clear();
 | 
			
		||||
                        merged_with[polyline_idx2] = merged_with[polyline_idx1];
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                } else if (anchor_length > SCALED_EPSILON) {
 | 
			
		||||
                    // Move along the perimeter, but don't take the whole arc.
 | 
			
		||||
                    take_limited(polyline1, contour, contour_params, cp1, cp2, false, anchor_length, line_half_width);
 | 
			
		||||
                    take_limited(polyline2, contour, contour_params, cp2, cp1, true,  anchor_length, line_half_width);
 | 
			
		||||
| 
						 | 
				
			
			@ -1360,7 +1397,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
            assert(contour[contour_point.point_idx] == polyline.points.front() || contour[contour_point.point_idx] == polyline.points.back());
 | 
			
		||||
            bool connected = false;
 | 
			
		||||
            for (float l : { std::min(lprev, lnext), std::max(lprev, lnext) }) {
 | 
			
		||||
                if (l == std::numeric_limits<float>::max() || l > anchor_length * 2.5)
 | 
			
		||||
                if (l == std::numeric_limits<float>::max() || l > anchor_length_max)
 | 
			
		||||
                    break;
 | 
			
		||||
                // Take the complete contour.
 | 
			
		||||
                bool      reversed      = l == lprev;
 | 
			
		||||
| 
						 | 
				
			
			@ -1392,7 +1429,7 @@ void Fill::connect_infill(Polylines &&infill_ordered, const std::vector<const Po
 | 
			
		|||
                connected = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            if (! connected) {
 | 
			
		||||
            if (! connected && anchor_length > SCALED_EPSILON) {
 | 
			
		||||
                // Which to take? One could optimize for:
 | 
			
		||||
                // 1) Shortest path
 | 
			
		||||
                // 2) Hook length
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,14 +34,15 @@ struct FillParams
 | 
			
		|||
{
 | 
			
		||||
    bool        full_infill() const { return density > 0.9999f; }
 | 
			
		||||
    // Don't connect the fill lines around the inner perimeter.
 | 
			
		||||
    bool        dont_connect() const { return anchor_length < 0.05f; }
 | 
			
		||||
    bool        dont_connect() const { return anchor_length_max < 0.05f; }
 | 
			
		||||
 | 
			
		||||
    // Fill density, fraction in <0, 1>
 | 
			
		||||
    float       density 		{ 0.f };
 | 
			
		||||
 | 
			
		||||
    // Length of an infill anchor along the perimeter.
 | 
			
		||||
    // 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
 | 
			
		||||
    float       anchor_length   { 1000.f };
 | 
			
		||||
    float       anchor_length       { 1000.f };
 | 
			
		||||
    float       anchor_length_max   { 1000.f };
 | 
			
		||||
 | 
			
		||||
    // Don't adjust spacing to fill the space evenly.
 | 
			
		||||
    bool        dont_adjust 	{ true };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,7 +39,7 @@ void FillConcentric::_fill_surface_single(
 | 
			
		|||
    size_t iPathFirst = polylines_out.size();
 | 
			
		||||
    Point last_pos(0, 0);
 | 
			
		||||
    for (const Polygon &loop : loops) {
 | 
			
		||||
        polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop)));
 | 
			
		||||
        polylines_out.push_back(loop.split_at_index(last_pos.nearest_point_index(loop.points)));
 | 
			
		||||
        last_pos = polylines_out.back().last_point();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue